commit a0f3d0edd763ab211ea345604120dc37abefe74b Author: Nielson Tschá Date: Tue Mar 16 13:35:01 2021 +0100 initial diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..5a513c2 Binary files /dev/null and b/.DS_Store differ diff --git a/CAD/Display3D_Druck.FCStd b/CAD/Display3D_Druck.FCStd new file mode 100644 index 0000000..39f49ba Binary files /dev/null and b/CAD/Display3D_Druck.FCStd differ diff --git a/CAD/DisplayHolder.FCStd b/CAD/DisplayHolder.FCStd new file mode 100644 index 0000000..74ae5a2 Binary files /dev/null and b/CAD/DisplayHolder.FCStd differ diff --git a/CAD/Halter_3D.stl b/CAD/Halter_3D.stl new file mode 100644 index 0000000..ab49efe Binary files /dev/null and b/CAD/Halter_3D.stl differ diff --git a/CAD/NWS_WMA_001_Feststellungsplate.pdf b/CAD/NWS_WMA_001_Feststellungsplate.pdf new file mode 100644 index 0000000..559a2a7 Binary files /dev/null and b/CAD/NWS_WMA_001_Feststellungsplate.pdf differ diff --git a/CAD/NWS_WMA_002_Bottom_Plate.pdf b/CAD/NWS_WMA_002_Bottom_Plate.pdf new file mode 100644 index 0000000..90a8ccd Binary files /dev/null and b/CAD/NWS_WMA_002_Bottom_Plate.pdf differ diff --git a/CAD/NWS_WMA_003_Back_Plate.pdf b/CAD/NWS_WMA_003_Back_Plate.pdf new file mode 100644 index 0000000..fc7c65d Binary files /dev/null and b/CAD/NWS_WMA_003_Back_Plate.pdf differ diff --git a/CAD/NWS_WMA_004_Mounting_View.pdf b/CAD/NWS_WMA_004_Mounting_View.pdf new file mode 100644 index 0000000..763b8f6 Binary files /dev/null and b/CAD/NWS_WMA_004_Mounting_View.pdf differ diff --git a/CAD/NWS_WMA_005_Complete_View.pdf b/CAD/NWS_WMA_005_Complete_View.pdf new file mode 100644 index 0000000..0c0ab65 Binary files /dev/null and b/CAD/NWS_WMA_005_Complete_View.pdf differ diff --git a/Display/.DS_Store b/Display/.DS_Store new file mode 100644 index 0000000..be7a180 Binary files /dev/null and b/Display/.DS_Store differ diff --git a/Display/.gitignore b/Display/.gitignore new file mode 100644 index 0000000..df2219c --- /dev/null +++ b/Display/.gitignore @@ -0,0 +1 @@ +*pyc \ No newline at end of file diff --git a/Display/.venv/bin/activate b/Display/.venv/bin/activate new file mode 100644 index 0000000..12438a0 --- /dev/null +++ b/Display/.venv/bin/activate @@ -0,0 +1,76 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r + fi + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + if [ ! "$1" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="/home/rusticus/Dokumente/Windmessanlage/Software/Display/.venv" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + if [ "x(.venv) " != x ] ; then + PS1="(.venv) ${PS1:-}" + else + if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1" + else + PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1" + fi + fi + export PS1 +fi + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r +fi diff --git a/Display/.venv/bin/activate.csh b/Display/.venv/bin/activate.csh new file mode 100644 index 0000000..67568d0 --- /dev/null +++ b/Display/.venv/bin/activate.csh @@ -0,0 +1,37 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/home/rusticus/Dokumente/Windmessanlage/Software/Display/.venv" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + if (".venv" != "") then + set env_name = ".venv" + else + if (`basename "VIRTUAL_ENV"` == "__") then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + set env_name = `basename \`dirname "$VIRTUAL_ENV"\`` + else + set env_name = `basename "$VIRTUAL_ENV"` + endif + endif + set prompt = "[$env_name] $prompt" + unset env_name +endif + +alias pydoc python -m pydoc + +rehash diff --git a/Display/.venv/bin/activate.fish b/Display/.venv/bin/activate.fish new file mode 100644 index 0000000..40869f9 --- /dev/null +++ b/Display/.venv/bin/activate.fish @@ -0,0 +1,75 @@ +# This file must be used with ". bin/activate.fish" *from fish* (http://fishshell.org) +# you cannot run it directly + +function deactivate -d "Exit virtualenv and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + functions -e fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + + set -e VIRTUAL_ENV + if test "$argv[1]" != "nondestructive" + # Self destruct! + functions -e deactivate + end +end + +# unset irrelevant variables +deactivate nondestructive + +set -gx VIRTUAL_ENV "/home/rusticus/Dokumente/Windmessanlage/Software/Display/.venv" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# unset PYTHONHOME if set +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # save the current fish_prompt function as the function _old_fish_prompt + functions -c fish_prompt _old_fish_prompt + + # with the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command + set -l old_status $status + + # Prompt override? + if test -n "(.venv) " + printf "%s%s" "(.venv) " (set_color normal) + else + # ...Otherwise, prepend env + set -l _checkbase (basename "$VIRTUAL_ENV") + if test $_checkbase = "__" + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + printf "%s[%s]%s " (set_color -b blue white) (basename (dirname "$VIRTUAL_ENV")) (set_color normal) + else + printf "%s(%s)%s" (set_color -b blue white) (basename "$VIRTUAL_ENV") (set_color normal) + end + end + + # Restore the return status of the previous command. + echo "exit $old_status" | . + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/Display/.venv/bin/easy_install b/Display/.venv/bin/easy_install new file mode 100755 index 0000000..73dfe6f --- /dev/null +++ b/Display/.venv/bin/easy_install @@ -0,0 +1,10 @@ +#!/home/rusticus/Dokumente/Windmessanlage/Software/Display/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from setuptools.command.easy_install import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/Display/.venv/bin/easy_install-3.7 b/Display/.venv/bin/easy_install-3.7 new file mode 100755 index 0000000..73dfe6f --- /dev/null +++ b/Display/.venv/bin/easy_install-3.7 @@ -0,0 +1,10 @@ +#!/home/rusticus/Dokumente/Windmessanlage/Software/Display/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from setuptools.command.easy_install import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/Display/.venv/bin/epylint b/Display/.venv/bin/epylint new file mode 100755 index 0000000..efe3406 --- /dev/null +++ b/Display/.venv/bin/epylint @@ -0,0 +1,10 @@ +#!/home/rusticus/Dokumente/Windmessanlage/Software/Display/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys + +from pylint import run_epylint + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(run_epylint()) diff --git a/Display/.venv/bin/isort b/Display/.venv/bin/isort new file mode 100755 index 0000000..6a47e2b --- /dev/null +++ b/Display/.venv/bin/isort @@ -0,0 +1,10 @@ +#!/home/rusticus/Dokumente/Windmessanlage/Software/Display/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys + +from isort.main import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/Display/.venv/bin/pip b/Display/.venv/bin/pip new file mode 100755 index 0000000..15e157f --- /dev/null +++ b/Display/.venv/bin/pip @@ -0,0 +1,10 @@ +#!/home/rusticus/Dokumente/Windmessanlage/Software/Display/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pip._internal import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/Display/.venv/bin/pip3 b/Display/.venv/bin/pip3 new file mode 100755 index 0000000..15e157f --- /dev/null +++ b/Display/.venv/bin/pip3 @@ -0,0 +1,10 @@ +#!/home/rusticus/Dokumente/Windmessanlage/Software/Display/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pip._internal import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/Display/.venv/bin/pip3.7 b/Display/.venv/bin/pip3.7 new file mode 100755 index 0000000..15e157f --- /dev/null +++ b/Display/.venv/bin/pip3.7 @@ -0,0 +1,10 @@ +#!/home/rusticus/Dokumente/Windmessanlage/Software/Display/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pip._internal import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/Display/.venv/bin/pylint b/Display/.venv/bin/pylint new file mode 100755 index 0000000..adb3162 --- /dev/null +++ b/Display/.venv/bin/pylint @@ -0,0 +1,10 @@ +#!/home/rusticus/Dokumente/Windmessanlage/Software/Display/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys + +from pylint import run_pylint + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(run_pylint()) diff --git a/Display/.venv/bin/pyreverse b/Display/.venv/bin/pyreverse new file mode 100755 index 0000000..7a20822 --- /dev/null +++ b/Display/.venv/bin/pyreverse @@ -0,0 +1,10 @@ +#!/home/rusticus/Dokumente/Windmessanlage/Software/Display/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys + +from pylint import run_pyreverse + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(run_pyreverse()) diff --git a/Display/.venv/bin/python b/Display/.venv/bin/python new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/Display/.venv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/Display/.venv/bin/python3 b/Display/.venv/bin/python3 new file mode 120000 index 0000000..ae65fda --- /dev/null +++ b/Display/.venv/bin/python3 @@ -0,0 +1 @@ +/usr/bin/python3 \ No newline at end of file diff --git a/Display/.venv/bin/symilar b/Display/.venv/bin/symilar new file mode 100755 index 0000000..44b7467 --- /dev/null +++ b/Display/.venv/bin/symilar @@ -0,0 +1,10 @@ +#!/home/rusticus/Dokumente/Windmessanlage/Software/Display/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys + +from pylint import run_symilar + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(run_symilar()) diff --git a/Display/.venv/include/site/python3.7/pygame/_camera.h b/Display/.venv/include/site/python3.7/pygame/_camera.h new file mode 100644 index 0000000..68ae989 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/_camera.h @@ -0,0 +1,27 @@ +/* + pygame - Python Game Library + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef _CAMERA_H +#define _CAMERA_H + +#include "_pygame.h" +#include "camera.h" + +#endif + diff --git a/Display/.venv/include/site/python3.7/pygame/_pygame.h b/Display/.venv/include/site/python3.7/pygame/_pygame.h new file mode 100644 index 0000000..e3807d2 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/_pygame.h @@ -0,0 +1,239 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +/* This will use PYGAMEAPI_EXTERN_SLOTS instead + * of PYGAMEAPI_DEFINE_SLOTS for base modules. + */ +#ifndef _PYGAME_INTERNAL_H +#define _PYGAME_INTERNAL_H + +#include "pgplatform.h" +#include +#include + +/* IS_SDLv1 is 1 if SDL 1.x.x, 0 otherwise */ +/* IS_SDLv2 is 1 if at least SDL 2.0.0, 0 otherwise */ +#if (SDL_VERSION_ATLEAST(2, 0, 0)) +#define IS_SDLv2 1 +#define IS_SDLv1 0 +#else +#define IS_SDLv2 0 +#define IS_SDLv1 1 +#endif + +/*#if IS_SDLv1 && PG_MAJOR_VERSION >= 2 +#error pygame 2 requires SDL 2 +#endif*/ + +#if SDL_VERSION_ATLEAST(2, 0, 0) +/* SDL 1.2 constants removed from SDL 2 */ +typedef enum { + SDL_HWSURFACE = 0, + SDL_RESIZABLE = SDL_WINDOW_RESIZABLE, + SDL_ASYNCBLIT = 0, + SDL_OPENGL = SDL_WINDOW_OPENGL, + SDL_OPENGLBLIT = 0, + SDL_ANYFORMAT = 0, + SDL_HWPALETTE = 0, + SDL_DOUBLEBUF = 0, + SDL_FULLSCREEN = SDL_WINDOW_FULLSCREEN, + SDL_HWACCEL = 0, + SDL_SRCCOLORKEY = 0, + SDL_RLEACCELOK = 0, + SDL_SRCALPHA = 0, + SDL_NOFRAME = SDL_WINDOW_BORDERLESS, + SDL_GL_SWAP_CONTROL = 0, + TIMER_RESOLUTION = 0 +} PygameVideoFlags; + +/* the wheel button constants were removed from SDL 2 */ +typedef enum { + PGM_BUTTON_LEFT = SDL_BUTTON_LEFT, + PGM_BUTTON_RIGHT = SDL_BUTTON_RIGHT, + PGM_BUTTON_MIDDLE = SDL_BUTTON_MIDDLE, + PGM_BUTTON_WHEELUP = 4, + PGM_BUTTON_WHEELDOWN = 5, + PGM_BUTTON_X1 = SDL_BUTTON_X1 + 2, + PGM_BUTTON_X2 = SDL_BUTTON_X2 + 2, + PGM_BUTTON_KEEP = 0x80 +} PygameMouseFlags; + +typedef enum { + /* Any SDL_* events here are for backward compatibility. */ + SDL_NOEVENT = 0, + + /* pygame events */ + PGE_EVENTBEGIN = SDL_USEREVENT, /* Not an event. Indicates start of pygame events. */ + SDL_ACTIVEEVENT = PGE_EVENTBEGIN, + SDL_VIDEORESIZE, + SDL_VIDEOEXPOSE, + PGE_KEYREPEAT, + PGE_MIDIIN, + PGE_MIDIOUT, + PGE_EVENTEND, /* Not an event. Indicates end of pygame events. */ + + /* User event range. */ + /* SDL 1.2 allowed for 8 user defined events. */ + PGE_USEREVENT = PGE_EVENTEND, + PG_NUMEVENTS = PGE_USEREVENT + 0x2000 /* Not an event. Indicates end of user events. */ +} PygameEventCode; + +#define PGE_NUMRESERVED (PGE_EVENTEND - PGE_EVENTBEGIN) + +typedef enum { + SDL_APPFOCUSMOUSE, + SDL_APPINPUTFOCUS, + SDL_APPACTIVE +} PygameAppCode; + +/* Surface flags: based on SDL 1.2 flags */ +typedef enum { + PGS_SWSURFACE = 0x00000000, + PGS_HWSURFACE = 0x00000001, + PGS_ASYNCBLIT = 0x00000004, + + PGS_ANYFORMAT = 0x10000000, + PGS_HWPALETTE = 0x20000000, + PGS_DOUBLEBUF = 0x40000000, + PGS_FULLSCREEN = 0x80000000, + PGS_SCALED = 0x00000200, + + PGS_OPENGL = 0x00000002, + PGS_OPENGLBLIT = 0x0000000A, + PGS_RESIZABLE = 0x00000010, + PGS_NOFRAME = 0x00000020, + PGS_SHOWN = 0x00000040, /* Added from SDL 2 */ + PGS_HIDDEN = 0x00000080, /* Added from SDL 2 */ + + PGS_HWACCEL = 0x00000100, + PGS_SRCCOLORKEY = 0x00001000, + PGS_RLEACCELOK = 0x00002000, + PGS_RLEACCEL = 0x00004000, + PGS_SRCALPHA = 0x00010000, + PGS_PREALLOC = 0x01000000 +} PygameSurfaceFlags; +#else /* ~SDL_VERSION_ATLEAST(2, 0, 0) */ +/* To maintain SDL 1.2 build support. */ +#define PGE_USEREVENT SDL_USEREVENT +#define PG_NUMEVENTS SDL_NUMEVENTS +/* These midi events were originally defined in midi.py. + * Note: They are outside the SDL_USEREVENT/SDL_NUMEVENTS event range for + * SDL 1.2. */ +#define PGE_MIDIIN PGE_USEREVENT + 10 +#define PGE_MIDIOUT PGE_USEREVENT + 11 +#endif /* ~SDL_VERSION_ATLEAST(2, 0, 0) */ + +#define RAISE(x, y) (PyErr_SetString((x), (y)), (PyObject *)NULL) + +/* + * Initialization checks + */ + +#define VIDEO_INIT_CHECK() \ + if (!SDL_WasInit(SDL_INIT_VIDEO)) \ + return RAISE(pgExc_SDLError, "video system not initialized") + +#define CDROM_INIT_CHECK() \ + if (!SDL_WasInit(SDL_INIT_CDROM)) \ + return RAISE(pgExc_SDLError, "cdrom system not initialized") + +#define JOYSTICK_INIT_CHECK() \ + if (!SDL_WasInit(SDL_INIT_JOYSTICK)) \ + return RAISE(pgExc_SDLError, "joystick system not initialized") + +/* thread check */ +#ifdef WITH_THREAD +#define PG_CHECK_THREADS() (1) +#else /* ~WITH_THREAD */ +#define PG_CHECK_THREADS() \ + (RAISE(PyExc_NotImplementedError, \ + "Python built without thread support")) +#endif /* ~WITH_THREAD */ + +#define PyType_Init(x) (((x).ob_type) = &PyType_Type) + +/* + * event module internals + */ +struct pgEventObject { + PyObject_HEAD int type; + PyObject *dict; +}; + +/* + * surflock module internals + */ +typedef struct { + PyObject_HEAD PyObject *surface; + PyObject *lockobj; + PyObject *weakrefs; +} pgLifetimeLockObject; + +/* + * surface module internals + */ +struct pgSubSurface_Data { + PyObject *owner; + int pixeloffset; + int offsetx, offsety; +}; + +/* + * color module internals + */ +struct pgColorObject { + PyObject_HEAD + Uint8 data[4]; + Uint8 len; +}; + +/* + * include public API + */ +#include "include/_pygame.h" + +#include "pgimport.h" + +/* Slot counts. + * Remember to keep these constants up to date. + */ + +#define PYGAMEAPI_RECT_NUMSLOTS 4 +#define PYGAMEAPI_JOYSTICK_NUMSLOTS 2 +#define PYGAMEAPI_DISPLAY_NUMSLOTS 2 +#define PYGAMEAPI_SURFACE_NUMSLOTS 4 +#define PYGAMEAPI_SURFLOCK_NUMSLOTS 8 +#define PYGAMEAPI_RWOBJECT_NUMSLOTS 6 +#define PYGAMEAPI_PIXELARRAY_NUMSLOTS 2 +#define PYGAMEAPI_COLOR_NUMSLOTS 5 +#define PYGAMEAPI_MATH_NUMSLOTS 2 +#define PYGAMEAPI_CDROM_NUMSLOTS 2 + +#if PG_API_VERSION == 1 +#define PYGAMEAPI_BASE_NUMSLOTS 19 +#define PYGAMEAPI_EVENT_NUMSLOTS 4 +#else /* PG_API_VERSION == 2 */ +#define PYGAMEAPI_BASE_NUMSLOTS 23 +#define PYGAMEAPI_EVENT_NUMSLOTS 6 +#endif /* PG_API_VERSION == 2 */ + +#endif /* _PYGAME_INTERNAL_H */ diff --git a/Display/.venv/include/site/python3.7/pygame/_surface.h b/Display/.venv/include/site/python3.7/pygame/_surface.h new file mode 100644 index 0000000..016aac0 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/_surface.h @@ -0,0 +1,31 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + Copyright (C) 2007 Marcus von Appen + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +#ifndef _SURFACE_H +#define _SURFACE_H + +#include "_pygame.h" +#include "surface.h" + +#endif + diff --git a/Display/.venv/include/site/python3.7/pygame/camera.h b/Display/.venv/include/site/python3.7/pygame/camera.h new file mode 100644 index 0000000..5001061 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/camera.h @@ -0,0 +1,205 @@ +#ifndef CAMERA_H +#define CAMERA_H +/* + pygame - Python Game Library + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "pygame.h" +#include "doc/camera_doc.h" + +#if defined(__unix__) + #include + #include + #include + #include + #include + + #include /* low-level i/o */ + #include + #include + #include + #include + #include + #include + #include + + /* on freebsd there is no asm/types */ + #ifdef linux + #include /* for videodev2.h */ + #endif + + #include +#elif defined(__APPLE__) + #include + /* We support OSX 10.6 and below. */ + #if __MAC_OS_X_VERSION_MAX_ALLOWED <= 1060 + #define PYGAME_MAC_CAMERA_OLD 1 + #endif +#endif + +#if defined(PYGAME_MAC_CAMERA_OLD) + #include + #include + #include +#endif + +/* some constants used which are not defined on non-v4l machines. */ +#ifndef V4L2_PIX_FMT_RGB24 + #define V4L2_PIX_FMT_RGB24 'RGB3' +#endif +#ifndef V4L2_PIX_FMT_RGB444 + #define V4L2_PIX_FMT_RGB444 'R444' +#endif +#ifndef V4L2_PIX_FMT_YUYV + #define V4L2_PIX_FMT_YUYV 'YUYV' +#endif + +#define CLEAR(x) memset (&(x), 0, sizeof (x)) +#define SAT(c) if (c & (~255)) { if (c < 0) c = 0; else c = 255; } +#define SAT2(c) ((c) & (~255) ? ((c) < 0 ? 0 : 255) : (c)) +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 +#define RGB_OUT 1 +#define YUV_OUT 2 +#define HSV_OUT 4 +#define CAM_V4L 1 /* deprecated. the incomplete support in pygame was removed */ +#define CAM_V4L2 2 + +struct buffer { + void * start; + size_t length; +}; + +#if defined(__unix__) +typedef struct pgCameraObject { + PyObject_HEAD + char* device_name; + int camera_type; + unsigned long pixelformat; + unsigned int color_out; + struct buffer* buffers; + unsigned int n_buffers; + int width; + int height; + int size; + int hflip; + int vflip; + int brightness; + int fd; +} pgCameraObject; +#elif defined(PYGAME_MAC_CAMERA_OLD) +typedef struct pgCameraObject { + PyObject_HEAD + char* device_name; /* unique name of the device */ + OSType pixelformat; + unsigned int color_out; + SeqGrabComponent component; /* A type used by the Sequence Grabber API */ + SGChannel channel; /* Channel of the Sequence Grabber */ + GWorldPtr gworld; /* Pointer to the struct that holds the data of the captured image */ + Rect boundsRect; /* bounds of the image frame */ + long size; /* size of the image in our buffer to draw */ + int hflip; + int vflip; + short depth; + struct buffer pixels; + //struct buffer tmp_pixels /* place where the flipped image in temporarily stored if hflip or vflip is true.*/ +} pgCameraObject; + +#else +/* generic definition. +*/ + +typedef struct pgCameraObject { + PyObject_HEAD + char* device_name; + int camera_type; + unsigned long pixelformat; + unsigned int color_out; + struct buffer* buffers; + unsigned int n_buffers; + int width; + int height; + int size; + int hflip; + int vflip; + int brightness; + int fd; +} pgCameraObject; +#endif + +/* internal functions for colorspace conversion */ +void colorspace (SDL_Surface *src, SDL_Surface *dst, int cspace); +void rgb24_to_rgb (const void* src, void* dst, int length, SDL_PixelFormat* format); +void rgb444_to_rgb (const void* src, void* dst, int length, SDL_PixelFormat* format); +void rgb_to_yuv (const void* src, void* dst, int length, + unsigned long source, SDL_PixelFormat* format); +void rgb_to_hsv (const void* src, void* dst, int length, + unsigned long source, SDL_PixelFormat* format); +void yuyv_to_rgb (const void* src, void* dst, int length, SDL_PixelFormat* format); +void yuyv_to_yuv (const void* src, void* dst, int length, SDL_PixelFormat* format); +void uyvy_to_rgb (const void* src, void* dst, int length, SDL_PixelFormat* format); +void uyvy_to_yuv (const void* src, void* dst, int length, SDL_PixelFormat* format); +void sbggr8_to_rgb (const void* src, void* dst, int width, int height, + SDL_PixelFormat* format); +void yuv420_to_rgb (const void* src, void* dst, int width, int height, + SDL_PixelFormat* format); +void yuv420_to_yuv (const void* src, void* dst, int width, int height, + SDL_PixelFormat* format); + +#if defined(__unix__) +/* internal functions specific to v4l2 */ +char** v4l2_list_cameras (int* num_devices); +int v4l2_get_control (int fd, int id, int *value); +int v4l2_set_control (int fd, int id, int value); +PyObject* v4l2_read_raw (pgCameraObject* self); +int v4l2_xioctl (int fd, int request, void *arg); +int v4l2_process_image (pgCameraObject* self, const void *image, + unsigned int buffer_size, SDL_Surface* surf); +int v4l2_query_buffer (pgCameraObject* self); +int v4l2_read_frame (pgCameraObject* self, SDL_Surface* surf); +int v4l2_stop_capturing (pgCameraObject* self); +int v4l2_start_capturing (pgCameraObject* self); +int v4l2_uninit_device (pgCameraObject* self); +int v4l2_init_mmap (pgCameraObject* self); +int v4l2_init_device (pgCameraObject* self); +int v4l2_close_device (pgCameraObject* self); +int v4l2_open_device (pgCameraObject* self); + +#elif defined(PYGAME_MAC_CAMERA_OLD) +/* internal functions specific to mac */ +char** mac_list_cameras(int* num_devices); +int mac_open_device (pgCameraObject* self); +int mac_init_device(pgCameraObject* self); +int mac_close_device (pgCameraObject* self); +int mac_start_capturing(pgCameraObject* self); +int mac_stop_capturing (pgCameraObject* self); + +int mac_get_control(pgCameraObject* self, int id, int* value); +int mac_set_control(pgCameraObject* self, int id, int value); + +PyObject* mac_read_raw(pgCameraObject *self); +int mac_read_frame(pgCameraObject* self, SDL_Surface* surf); +int mac_camera_idle(pgCameraObject* self); +int mac_copy_gworld_to_surface(pgCameraObject* self, SDL_Surface* surf); + +void flip_image(const void* image, void* flipped_image, int width, int height, + short depth, int hflip, int vflip); + +#endif + +#endif /* !CAMERA_H */ diff --git a/Display/.venv/include/site/python3.7/pygame/fastevents.h b/Display/.venv/include/site/python3.7/pygame/fastevents.h new file mode 100644 index 0000000..04098c3 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/fastevents.h @@ -0,0 +1,48 @@ +#ifndef _FASTEVENTS_H_ +#define _FASTEVENTS_H_ +/* + NET2 is a threaded, event based, network IO library for SDL. + Copyright (C) 2002 Bob Pendleton + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 + of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA + + If you do not wish to comply with the terms of the LGPL please + contact the author as other terms are available for a fee. + + Bob Pendleton + Bob@Pendleton.com +*/ + +#include "SDL.h" + +#ifdef __cplusplus +extern "C" { +#endif + + int FE_Init(void); // Initialize FE + void FE_Quit(void); // shutdown FE + + void FE_PumpEvents(void); // replacement for SDL_PumpEvents + int FE_PollEvent(SDL_Event *event); // replacement for SDL_PollEvent + int FE_WaitEvent(SDL_Event *event); // replacement for SDL_WaitEvent + int FE_PushEvent(SDL_Event *event); // replacement for SDL_PushEvent + + char *FE_GetError(void); // get the last error +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Display/.venv/include/site/python3.7/pygame/font.h b/Display/.venv/include/site/python3.7/pygame/font.h new file mode 100644 index 0000000..9878435 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/font.h @@ -0,0 +1,15 @@ +#ifndef PGFONT_INTERNAL_H +#define PGFONT_INTERNAL_H + +#include + +/* test font initialization */ +#define FONT_INIT_CHECK() \ + if(!(*(int*)PyFONT_C_API[2])) \ + return RAISE(pgExc_SDLError, "font system not initialized") + +#include "include/pygame_font.h" + +#define PYGAMEAPI_FONT_NUMSLOTS 3 + +#endif /* ~PGFONT_INTERNAL_H */ diff --git a/Display/.venv/include/site/python3.7/pygame/freetype.h b/Display/.venv/include/site/python3.7/pygame/freetype.h new file mode 100644 index 0000000..a7c593b --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/freetype.h @@ -0,0 +1,121 @@ +/* + pygame - Python Game Library + Copyright (C) 2009 Vicent Marti + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#ifndef _PYGAME_FREETYPE_INTERNAL_H_ +#define _PYGAME_FREETYPE_INTERNAL_H_ + +#define PGFT_PYGAME1_COMPAT +#define HAVE_PYGAME_SDL_VIDEO +#define HAVE_PYGAME_SDL_RWOPS + +#include "pgcompat.h" +#include "pgplatform.h" +#include + +#include +#include FT_FREETYPE_H +#include FT_CACHE_H +#include FT_XFREE86_H +#include FT_TRIGONOMETRY_H + +/********************************************************** + * Global module constants + **********************************************************/ + +/* Render styles */ +#define FT_STYLE_NORMAL 0x00 +#define FT_STYLE_STRONG 0x01 +#define FT_STYLE_OBLIQUE 0x02 +#define FT_STYLE_UNDERLINE 0x04 +#define FT_STYLE_WIDE 0x08 +#define FT_STYLE_DEFAULT 0xFF + +/* Bounding box modes */ +#define FT_BBOX_EXACT FT_GLYPH_BBOX_SUBPIXELS +#define FT_BBOX_EXACT_GRIDFIT FT_GLYPH_BBOX_GRIDFIT +#define FT_BBOX_PIXEL FT_GLYPH_BBOX_TRUNCATE +#define FT_BBOX_PIXEL_GRIDFIT FT_GLYPH_BBOX_PIXELS + +/* Rendering flags */ +#define FT_RFLAG_NONE (0) +#define FT_RFLAG_ANTIALIAS (1 << 0) +#define FT_RFLAG_AUTOHINT (1 << 1) +#define FT_RFLAG_VERTICAL (1 << 2) +#define FT_RFLAG_HINTED (1 << 3) +#define FT_RFLAG_KERNING (1 << 4) +#define FT_RFLAG_TRANSFORM (1 << 5) +#define FT_RFLAG_PAD (1 << 6) +#define FT_RFLAG_ORIGIN (1 << 7) +#define FT_RFLAG_UCS4 (1 << 8) +#define FT_RFLAG_USE_BITMAP_STRIKES (1 << 9) +#define FT_RFLAG_DEFAULTS (FT_RFLAG_HINTED | \ + FT_RFLAG_USE_BITMAP_STRIKES | \ + FT_RFLAG_ANTIALIAS) + + +#define FT_RENDER_NEWBYTEARRAY 0x0 +#define FT_RENDER_NEWSURFACE 0x1 +#define FT_RENDER_EXISTINGSURFACE 0x2 + +/********************************************************** + * Global module types + **********************************************************/ + +typedef struct _scale_s { + FT_UInt x, y; +} Scale_t; +typedef FT_Angle Angle_t; + +struct fontinternals_; +struct freetypeinstance_; + +typedef struct { + FT_Long font_index; + FT_Open_Args open_args; +} pgFontId; + +typedef struct { + PyObject_HEAD + pgFontId id; + PyObject *path; + int is_scalable; + + Scale_t face_size; + FT_Int16 style; + FT_Int16 render_flags; + double strength; + double underline_adjustment; + FT_UInt resolution; + Angle_t rotation; + FT_Matrix transform; + FT_Byte fgcolor[4]; + + struct freetypeinstance_ *freetype; /* Personal reference */ + struct fontinternals_ *_internals; +} pgFontObject; + +#define pgFont_IS_ALIVE(o) \ + (((pgFontObject *)(o))->_internals != 0) + +/* import public API */ +#include "include/pygame_freetype.h" + +#define PYGAMEAPI_FREETYPE_NUMSLOTS 2 + +#endif /* ~_PYGAME_FREETYPE_INTERNAL_H_ */ diff --git a/Display/.venv/include/site/python3.7/pygame/include/_pygame.h b/Display/.venv/include/site/python3.7/pygame/include/_pygame.h new file mode 100644 index 0000000..11bd42d --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/include/_pygame.h @@ -0,0 +1,632 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +#ifndef _PYGAME_H +#define _PYGAME_H + +/** This header file includes all the definitions for the + ** base pygame extensions. This header only requires + ** Python includes (and SDL.h for functions that use SDL types). + ** The reason for functions prototyped with #define's is + ** to allow for maximum Python portability. It also uses + ** Python as the runtime linker, which allows for late binding. + '' For more information on this style of development, read + ** the Python docs on this subject. + ** http://www.python.org/doc/current/ext/using-cobjects.html + ** + ** If using this to build your own derived extensions, + ** you'll see that the functions available here are mainly + ** used to help convert between python objects and SDL objects. + ** Since this library doesn't add a lot of functionality to + ** the SDL library, it doesn't need to offer a lot either. + ** + ** When initializing your extension module, you must manually + ** import the modules you want to use. (this is the part about + ** using python as the runtime linker). Each module has its + ** own import_xxx() routine. You need to perform this import + ** after you have initialized your own module, and before + ** you call any routines from that module. Since every module + ** in pygame does this, there are plenty of examples. + ** + ** The base module does include some useful conversion routines + ** that you are free to use in your own extension. + **/ + +#include "pgplatform.h" +#include + + +/* version macros (defined since version 1.9.5) */ +#define PG_MAJOR_VERSION 2 +#define PG_MINOR_VERSION 0 +#define PG_PATCH_VERSION 0 +#define PG_VERSIONNUM(MAJOR, MINOR, PATCH) (1000*(MAJOR) + 100*(MINOR) + (PATCH)) +#define PG_VERSION_ATLEAST(MAJOR, MINOR, PATCH) \ + (PG_VERSIONNUM(PG_MAJOR_VERSION, PG_MINOR_VERSION, PG_PATCH_VERSION) >= \ + PG_VERSIONNUM(MAJOR, MINOR, PATCH)) + +/* SDL 1.x/2.x and pygame 1.x/2.x + */ +#if defined(SDL_VERSION_ATLEAST) +#if (SDL_VERSION_ATLEAST(2, 0, 0)) +#define PG_API_VERSION 2 +#else /* SDL 1 */ +/* for now: allow pygame 2 to be compiled with SDL 1. */ +#define PG_API_VERSION 1 +#endif /* SDL 1 */ +#else /* NO SDL */ +#define PG_API_VERSION ((PG_MAJOR_VERSION == 1) ? 1 : 2) +#endif /* NO SDL */ + +#include "pgcompat.h" + + +/* Flag indicating a pg_buffer; used for assertions within callbacks */ +#ifndef NDEBUG +#define PyBUF_PYGAME 0x4000 +#endif +#define PyBUF_HAS_FLAG(f, F) (((f) & (F)) == (F)) + +/* Array information exchange struct C type; inherits from Py_buffer + * + * Pygame uses its own Py_buffer derived C struct as an internal representation + * of an imported array buffer. The extended Py_buffer allows for a + * per-instance release callback, + */ +typedef void (*pybuffer_releaseproc)(Py_buffer *); + +typedef struct pg_bufferinfo_s { + Py_buffer view; + PyObject *consumer; /* Input: Borrowed reference */ + pybuffer_releaseproc release_buffer; +} pg_buffer; + +#include "pgimport.h" + +/* + * BASE module + */ +#ifndef PYGAMEAPI_BASE_INTERNAL +#define pgExc_SDLError \ + ((PyObject *) \ + PYGAMEAPI_GET_SLOT(base, 0)) + +#define pg_RegisterQuit \ + (*(void (*)(void (*)(void))) \ + PYGAMEAPI_GET_SLOT(base, 1)) + +#define pg_IntFromObj \ + (*(int (*)(PyObject *, int *)) \ + PYGAMEAPI_GET_SLOT(base, 2)) + +#define pg_IntFromObjIndex \ + (*(int (*)(PyObject *, int, int *)) \ + PYGAMEAPI_GET_SLOT(base, 3)) + +#define pg_TwoIntsFromObj \ + (*(int (*)(PyObject *, int *, int *)) \ + PYGAMEAPI_GET_SLOT(base, 4)) + +#define pg_FloatFromObj \ + (*(int (*)(PyObject *, float *)) \ + PYGAMEAPI_GET_SLOT(base, 5)) + +#define pg_FloatFromObjIndex \ + (*(int (*)(PyObject *, int, float *)) \ + PYGAMEAPI_GET_SLOT(base, 6)) + +#define pg_TwoFloatsFromObj \ + (*(int (*)(PyObject *, float *, float *)) \ + PYGAMEAPI_GET_SLOT(base, 7)) + +#define pg_UintFromObj \ + (*(int (*)(PyObject *, Uint32 *)) \ + PYGAMEAPI_GET_SLOT(base, 8)) + +#define pg_UintFromObjIndex \ + (*(int (*)(PyObject *, int, Uint32 *)) \ + PYGAMEAPI_GET_SLOT(base, 9)) + +#define pgVideo_AutoQuit \ + (*(void (*)(void)) \ + PYGAMEAPI_GET_SLOT(base, 10)) + +#define pgVideo_AutoInit \ + (*(int (*)(void)) \ + PYGAMEAPI_GET_SLOT(base, 11)) + +#define pg_RGBAFromObj \ + (*(int (*)(PyObject *, Uint8 *)) \ + PYGAMEAPI_GET_SLOT(base, 12)) + +#define pgBuffer_AsArrayInterface \ + (*(PyObject * (*)(Py_buffer *)) \ + PYGAMEAPI_GET_SLOT(base, 13)) + +#define pgBuffer_AsArrayStruct \ + (*(PyObject * (*)(Py_buffer *)) \ + PYGAMEAPI_GET_SLOT(base, 14)) + +#define pgObject_GetBuffer \ + (*(int (*)(PyObject *, pg_buffer *, int)) \ + PYGAMEAPI_GET_SLOT(base, 15)) + +#define pgBuffer_Release \ + (*(void (*)(pg_buffer *)) \ + PYGAMEAPI_GET_SLOT(base, 16)) + +#define pgDict_AsBuffer \ + (*(int (*)(pg_buffer *, PyObject *, int)) \ + PYGAMEAPI_GET_SLOT(base, 17)) + +#define pgExc_BufferError \ + ((PyObject *) \ + PYGAMEAPI_GET_SLOT(base, 18)) + +#if PG_API_VERSION == 2 +#define pg_GetDefaultWindow \ + (*(SDL_Window * (*)(void)) \ + PYGAMEAPI_GET_SLOT(base, 19)) + +#define pg_SetDefaultWindow \ + (*(void (*)(SDL_Window *)) \ + PYGAMEAPI_GET_SLOT(base, 20)) + +#define pg_GetDefaultWindowSurface \ + (*(PyObject * (*)(void)) \ + PYGAMEAPI_GET_SLOT(base, 21)) + +#define pg_SetDefaultWindowSurface \ + (*(void (*)(PyObject *)) \ + PYGAMEAPI_GET_SLOT(base, 22)) + +#endif /* PG_API_VERSION == 2 */ + +#define import_pygame_base() IMPORT_PYGAME_MODULE(base) +#endif /* ~PYGAMEAPI_BASE_INTERNAL */ + +/* + * RECT module + */ +#if !defined(SDL_VERSION_ATLEAST) || PG_API_VERSION == 1 +typedef struct { + int x, y; + int w, h; +} GAME_Rect; +#else /* SDL 2+ */ +typedef SDL_Rect GAME_Rect; +#endif /* SDL 2+ */ + +typedef struct { + PyObject_HEAD GAME_Rect r; + PyObject *weakreflist; +} pgRectObject; + +#define pgRect_AsRect(x) (((pgRectObject *)x)->r) +#ifndef PYGAMEAPI_RECT_INTERNAL +#define pgRect_Type \ + (*(PyTypeObject *) \ + PYGAMEAPI_GET_SLOT(rect, 0)) + +#define pgRect_Check(x) \ + ((x)->ob_type == &pgRect_Type) +#define pgRect_New \ + (*(PyObject * (*)(SDL_Rect *)) \ + PYGAMEAPI_GET_SLOT(rect, 1)) + +#define pgRect_New4 \ + (*(PyObject * (*)(int, int, int, int)) \ + PYGAMEAPI_GET_SLOT(rect, 2)) + +#define pgRect_FromObject \ + (*(GAME_Rect * (*)(PyObject *, GAME_Rect *)) \ + PYGAMEAPI_GET_SLOT(rect, 3)) + +#define import_pygame_rect() IMPORT_PYGAME_MODULE(rect) +#endif /* ~PYGAMEAPI_RECT_INTERNAL */ + +/* + * CDROM module + */ + +typedef struct { + PyObject_HEAD int id; +} pgCDObject; + +#define pgCD_AsID(x) (((pgCDObject *)x)->id) +#ifndef PYGAMEAPI_CDROM_INTERNAL +#define pgCD_Type \ + (*(PyTypeObject *) \ + PYGAMEAPI_GET_SLOT(cdrom, 0)) + +#define pgCD_Check(x) \ + ((x)->ob_type == &pgCD_Type) +#define pgCD_New \ + (*(PyObject * (*)(int)) \ + PYGAMEAPI_GET_SLOT(cdrom, 1)) + +#define import_pygame_cd() IMPORT_PYGAME_MODULE(cdrom) +#endif + +/* + * JOYSTICK module + */ +typedef struct { + PyObject_HEAD int id; +} pgJoystickObject; + +#define pgJoystick_AsID(x) (((pgJoystickObject *)x)->id) + +#ifndef PYGAMEAPI_JOYSTICK_INTERNAL +#define pgJoystick_Type \ + (*(PyTypeObject *) \ + PYGAMEAPI_GET_SLOT(joystick, 0)) + +#define pgJoystick_Check(x) \ + ((x)->ob_type == &pgJoystick_Type) +#define pgJoystick_New \ + (*(PyObject * (*)(int)) \ + PYGAMEAPI_GET_SLOT(joystick, 1)) + +#define import_pygame_joystick() IMPORT_PYGAME_MODULE(joystick) +#endif + +/* + * DISPLAY module + */ + +#if defined(SDL_VERSION_ATLEAST) + +#if PG_API_VERSION == 2 +typedef struct { + Uint32 hw_available:1; + Uint32 wm_available:1; + Uint32 blit_hw:1; + Uint32 blit_hw_CC:1; + Uint32 blit_hw_A:1; + Uint32 blit_sw:1; + Uint32 blit_sw_CC:1; + Uint32 blit_sw_A:1; + Uint32 blit_fill:1; + Uint32 video_mem; + SDL_PixelFormat *vfmt; + SDL_PixelFormat vfmt_data; + int current_w; + int current_h; +} pg_VideoInfo; +#endif /* PG_API_VERSION == 2 */ + +typedef struct { +#if PG_API_VERSION == 1 + PyObject_HEAD SDL_VideoInfo info; +#else + PyObject_HEAD pg_VideoInfo info; +#endif +} pgVidInfoObject; + +#define pgVidInfo_AsVidInfo(x) (((pgVidInfoObject *)x)->info) +#endif /* defined(SDL_VERSION_ATLEAST) */ + +#ifndef PYGAMEAPI_DISPLAY_INTERNAL +#define pgVidInfo_Type \ + (*(PyTypeObject *) \ + PYGAMEAPI_GET_SLOT(display, 0)) + +#define pgVidInfo_Check(x) \ + ((x)->ob_type == &pgVidInfo_Type) + +#if PG_API_VERSION == 1 +#define pgVidInfo_New \ + (*(PyObject * (*)(SDL_VideoInfo *)) \ + PYGAMEAPI_GET_SLOT(display, 1)) +#else +#define pgVidInfo_New \ + (*(PyObject * (*)(pg_VideoInfo *)) \ + PYGAMEAPI_GET_SLOT(display, 1)) +#endif + +#define import_pygame_display() IMPORT_PYGAME_MODULE(display) +#endif /* ~PYGAMEAPI_DISPLAY_INTERNAL */ + +/* + * SURFACE module + */ +struct pgSubSurface_Data; +struct SDL_Surface; + +typedef struct { + PyObject_HEAD struct SDL_Surface *surf; +#if PG_API_VERSION == 2 + int owner; +#endif /* PG_API_VERSION == 2 */ + struct pgSubSurface_Data *subsurface; /* ptr to subsurface data (if a + * subsurface)*/ + PyObject *weakreflist; + PyObject *locklist; + PyObject *dependency; +} pgSurfaceObject; +#define pgSurface_AsSurface(x) (((pgSurfaceObject *)x)->surf) + +#ifndef PYGAMEAPI_SURFACE_INTERNAL +#define pgSurface_Type \ + (*(PyTypeObject *) \ + PYGAMEAPI_GET_SLOT(surface, 0)) + +#define pgSurface_Check(x) \ + (PyObject_IsInstance((x), (PyObject *) &pgSurface_Type)) +#if PG_API_VERSION == 1 +#define pgSurface_New \ + (*(PyObject * (*)(SDL_Surface *)) \ + PYGAMEAPI_GET_SLOT(surface, 1)) + +#define pgSurface_SetSurface \ + (*(int (*)(PyObject *, SDL_Surface *)) \ + PYGAMEAPI_GET_SLOT(surface, 3)) + +#else /* PG_API_VERSION == 2 */ +#define pgSurface_New2 \ + (*(PyObject * (*)(SDL_Surface *, int)) \ + PYGAMEAPI_GET_SLOT(surface, 1)) + +#define pgSurface_SetSurface \ + (*(int (*)(PyObject *, SDL_Surface *, int)) \ + PYGAMEAPI_GET_SLOT(surface, 3)) + +#endif /* PG_API_VERSION == 2 */ +#define pgSurface_Blit \ + (*(int (*)(PyObject *, PyObject *, GAME_Rect *, GAME_Rect *, int)) \ + PYGAMEAPI_GET_SLOT(surface, 2)) + +#define import_pygame_surface() \ + do { \ + IMPORT_PYGAME_MODULE(surface); \ + if (PyErr_Occurred() != NULL) \ + break; \ + IMPORT_PYGAME_MODULE(surflock); \ + } while (0) + +#if PG_API_VERSION == 2 +#define pgSurface_New(surface) pgSurface_New2((surface), 1) +#define pgSurface_NewNoOwn(surface) pgSurface_New2((surface), 0) +#endif /* PG_API_VERSION == 2 */ + +#endif /* ~PYGAMEAPI_SURFACE_INTERNAL */ + +/* + * SURFLOCK module + * auto imported/initialized by surface + */ +#ifndef PYGAMEAPI_SURFLOCK_INTERNAL +#define pgLifetimeLock_Type \ + (*(PyTypeObject *) \ + PYGAMEAPI_GET_SLOT(surflock, 0)) + +#define pgLifetimeLock_Check(x) \ + ((x)->ob_type == &pgLifetimeLock_Type) + +#define pgSurface_Prep(x) \ + if (((pgSurfaceObject *)x)->subsurface) \ + (*(*(void (*)(PyObject *)) \ + PYGAMEAPI_GET_SLOT(surflock, 1)))(x) + +#define pgSurface_Unprep(x) \ + if (((pgSurfaceObject *)x)->subsurface) \ + (*(*(void (*)(PyObject *)) \ + PYGAMEAPI_GET_SLOT(surflock, 2)))(x) + +#define pgSurface_Lock \ + (*(int (*)(PyObject *)) \ + PYGAMEAPI_GET_SLOT(surflock, 3)) + +#define pgSurface_Unlock \ + (*(int (*)(PyObject *)) \ + PYGAMEAPI_GET_SLOT(surflock, 4)) + +#define pgSurface_LockBy \ + (*(int (*)(PyObject *, PyObject *)) \ + PYGAMEAPI_GET_SLOT(surflock, 5)) + +#define pgSurface_UnlockBy \ + (*(int (*)(PyObject *, PyObject *)) \ + PYGAMEAPI_GET_SLOT(surflock, 6)) + +#define pgSurface_LockLifetime \ + (*(PyObject * (*)(PyObject *, PyObject *)) \ + PYGAMEAPI_GET_SLOT(surflock, 7)) +#endif + +/* + * EVENT module + */ +typedef struct pgEventObject pgEventObject; + +#ifndef PYGAMEAPI_EVENT_INTERNAL +#define pgEvent_Type \ + (*(PyTypeObject *) \ + PYGAMEAPI_GET_SLOT(event, 0)) + +#define pgEvent_Check(x) \ + ((x)->ob_type == &pgEvent_Type) + +#define pgEvent_New \ + (*(PyObject * (*)(SDL_Event *)) \ + PYGAMEAPI_GET_SLOT(event, 1)) + +#define pgEvent_New2 \ + (*(PyObject * (*)(int, PyObject *)) \ + PYGAMEAPI_GET_SLOT(event, 2)) + +#define pgEvent_FillUserEvent \ + (*(int (*)(pgEventObject *, SDL_Event *)) \ + PYGAMEAPI_GET_SLOT(event, 3)) + +#if PG_API_VERSION == 2 +#define pg_EnableKeyRepeat \ + (*(int (*)(int, int)) \ + PYGAMEAPI_GET_SLOT(event, 4)) + +#define pg_GetKeyRepeat \ + (*(void (*)(int *, int *)) \ + PYGAMEAPI_GET_SLOT(event, 5)) +#endif /* PG_API_VERSION == 2 */ + +#define import_pygame_event() IMPORT_PYGAME_MODULE(event) +#endif + +/* + * RWOBJECT module + * the rwobject are only needed for C side work, not accessable from python. + */ +#ifndef PYGAMEAPI_RWOBJECT_INTERNAL +#define pgRWops_FromObject \ + (*(SDL_RWops * (*)(PyObject *)) \ + PYGAMEAPI_GET_SLOT(rwobject, 0)) + +#define pgRWops_IsFileObject \ + (*(int (*)(SDL_RWops *)) \ + PYGAMEAPI_GET_SLOT(rwobject, 1)) + +#define pg_EncodeFilePath \ + (*(PyObject * (*)(PyObject *, PyObject *)) \ + PYGAMEAPI_GET_SLOT(rwobject, 2)) + +#define pg_EncodeString \ + (*(PyObject * (*)(PyObject *, const char *, const char *, PyObject *)) \ + PYGAMEAPI_GET_SLOT(rwobject, 3)) + +#define pgRWops_FromFileObject \ + (*(SDL_RWops * (*)(PyObject *)) \ + PYGAMEAPI_GET_SLOT(rwobject, 4)) + +#define pgRWops_ReleaseObject \ + (*(int (*)(SDL_RWops *)) \ + PYGAMEAPI_GET_SLOT(rwobject, 5)) + +#define import_pygame_rwobject() IMPORT_PYGAME_MODULE(rwobject) + +#endif + +/* + * PixelArray module + */ +#ifndef PYGAMEAPI_PIXELARRAY_INTERNAL +#define PyPixelArray_Type \ + ((PyTypeObject *) \ + PYGAMEAPI_GET_SLOT(pixelarray, 0)) + +#define PyPixelArray_Check(x) \ + ((x)->ob_type == &PyPixelArray_Type) +#define PyPixelArray_New \ + (*(PyObject * (*)) \ + PYGAMEAPI_GET_SLOT(pixelarray, 1)) + +#define import_pygame_pixelarray() IMPORT_PYGAME_MODULE(pixelarray) +#endif /* PYGAMEAPI_PIXELARRAY_INTERNAL */ + +/* + * Color module + */ +typedef struct pgColorObject pgColorObject; + +#ifndef PYGAMEAPI_COLOR_INTERNAL +#define pgColor_Type (*(PyObject *) \ + PYGAMEAPI_GET_SLOT(color, 0)) + +#define pgColor_Check(x) \ + ((x)->ob_type == &pgColor_Type) +#define pgColor_New \ + (*(PyObject * (*)(Uint8 *)) \ + PYGAMEAPI_GET_SLOT(color, 1)) + +#define pgColor_NewLength \ + (*(PyObject * (*)(Uint8 *, Uint8)) \ + PYGAMEAPI_GET_SLOT(color, 3)) + +#define pg_RGBAFromColorObj \ + (*(int (*)(PyObject *, Uint8 *)) \ + PYGAMEAPI_GET_SLOT(color, 2)) + +#define pg_RGBAFromFuzzyColorObj \ + (*(int (*)(PyObject *, Uint8 *)) \ + PYGAMEAPI_GET_SLOT(color, 4)) + +#define pgColor_AsArray(x) (((pgColorObject *)x)->data) +#define pgColor_NumComponents(x) (((pgColorObject *)x)->len) + + +#define import_pygame_color() IMPORT_PYGAME_MODULE(color) +#endif /* PYGAMEAPI_COLOR_INTERNAL */ + +/* + * Math module + */ +#ifndef PYGAMEAPI_MATH_INTERNAL +#define pgVector2_Check(x) \ + ((x)->ob_type == (PyTypeObject *) \ + PYGAMEAPI_GET_SLOT(math, 0)) + +#define pgVector3_Check(x) \ + ((x)->ob_type == (PyTypeObject *) \ + PYGAMEAPI_GET_SLOT(math, 1)) +/* +#define pgVector2_New \ + (*(PyObject*(*)) \ + PYGAMEAPI_GET_SLOT(PyGAME_C_API, 1)) +*/ +#define import_pygame_math() IMPORT_PYGAME_MODULE(math) +#endif /* PYGAMEAPI_MATH_INTERNAL */ + +#define IMPORT_PYGAME_MODULE _IMPORT_PYGAME_MODULE + +/* + * base pygame API slots + * disable slots with NO_PYGAME_C_API + */ +#ifdef PYGAME_H +PYGAMEAPI_DEFINE_SLOTS(base); +PYGAMEAPI_DEFINE_SLOTS(rect); +PYGAMEAPI_DEFINE_SLOTS(cdrom); +PYGAMEAPI_DEFINE_SLOTS(joystick); +PYGAMEAPI_DEFINE_SLOTS(display); +PYGAMEAPI_DEFINE_SLOTS(surface); +PYGAMEAPI_DEFINE_SLOTS(surflock); +PYGAMEAPI_DEFINE_SLOTS(event); +PYGAMEAPI_DEFINE_SLOTS(rwobject); +PYGAMEAPI_DEFINE_SLOTS(pixelarray); +PYGAMEAPI_DEFINE_SLOTS(color); +PYGAMEAPI_DEFINE_SLOTS(math); +#else /* ~PYGAME_H */ +PYGAMEAPI_EXTERN_SLOTS(base); +PYGAMEAPI_EXTERN_SLOTS(rect); +PYGAMEAPI_EXTERN_SLOTS(cdrom); +PYGAMEAPI_EXTERN_SLOTS(joystick); +PYGAMEAPI_EXTERN_SLOTS(display); +PYGAMEAPI_EXTERN_SLOTS(surface); +PYGAMEAPI_EXTERN_SLOTS(surflock); +PYGAMEAPI_EXTERN_SLOTS(event); +PYGAMEAPI_EXTERN_SLOTS(rwobject); +PYGAMEAPI_EXTERN_SLOTS(pixelarray); +PYGAMEAPI_EXTERN_SLOTS(color); +PYGAMEAPI_EXTERN_SLOTS(math); +#endif /* ~PYGAME_H */ + +#endif /* PYGAME_H */ diff --git a/Display/.venv/include/site/python3.7/pygame/include/bitmask.h b/Display/.venv/include/site/python3.7/pygame/include/bitmask.h new file mode 100644 index 0000000..d95297e --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/include/bitmask.h @@ -0,0 +1,149 @@ +/* + Bitmask 1.7 - A pixel-perfect collision detection library. + + Copyright (C) 2002-2005 Ulf Ekstrom except for the bitcount + function which is copyright (C) Donald W. Gillies, 1992. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef BITMASK_H +#define BITMASK_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +/* Define INLINE for different compilers. If your compiler does not + support inlining then there might be a performance hit in + bitmask_overlap_area(). +*/ +#ifndef INLINE +# ifdef __GNUC__ +# define INLINE inline +# else +# ifdef _MSC_VER +# define INLINE __inline +# else +# define INLINE +# endif +# endif +#endif + +#define BITMASK_W unsigned long int +#define BITMASK_W_LEN (sizeof(BITMASK_W)*CHAR_BIT) +#define BITMASK_W_MASK (BITMASK_W_LEN - 1) +#define BITMASK_N(n) ((BITMASK_W)1 << (n)) + +typedef struct bitmask +{ + int w,h; + BITMASK_W bits[1]; +} bitmask_t; + +/* Creates a bitmask of width w and height h, where + w and h must both be greater than or equal to 0. + The mask is automatically cleared when created. + */ +bitmask_t *bitmask_create(int w, int h); + +/* Frees all the memory allocated by bitmask_create for m. */ +void bitmask_free(bitmask_t *m); + +/* Create a copy of the given bitmask. */ +bitmask_t *bitmask_copy(bitmask_t *m); + +/* Clears all bits in the mask */ +void bitmask_clear(bitmask_t *m); + +/* Sets all bits in the mask */ +void bitmask_fill(bitmask_t *m); + +/* Flips all bits in the mask */ +void bitmask_invert(bitmask_t *m); + +/* Counts the bits in the mask */ +unsigned int bitmask_count(bitmask_t *m); + +/* Returns nonzero if the bit at (x,y) is set. Coordinates start at + (0,0) */ +static INLINE int bitmask_getbit(const bitmask_t *m, int x, int y) +{ + return (m->bits[x/BITMASK_W_LEN*m->h + y] & BITMASK_N(x & BITMASK_W_MASK)) != 0; +} + +/* Sets the bit at (x,y) */ +static INLINE void bitmask_setbit(bitmask_t *m, int x, int y) +{ + m->bits[x/BITMASK_W_LEN*m->h + y] |= BITMASK_N(x & BITMASK_W_MASK); +} + +/* Clears the bit at (x,y) */ +static INLINE void bitmask_clearbit(bitmask_t *m, int x, int y) +{ + m->bits[x/BITMASK_W_LEN*m->h + y] &= ~BITMASK_N(x & BITMASK_W_MASK); +} + +/* Returns nonzero if the masks overlap with the given offset. + The overlap tests uses the following offsets (which may be negative): + + +----+----------.. + |A | yoffset + | +-+----------.. + +--|B + |xoffset + | | + : : +*/ +int bitmask_overlap(const bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset); + +/* Like bitmask_overlap(), but will also give a point of intersection. + x and y are given in the coordinates of mask a, and are untouched + if there is no overlap. */ +int bitmask_overlap_pos(const bitmask_t *a, const bitmask_t *b, + int xoffset, int yoffset, int *x, int *y); + +/* Returns the number of overlapping 'pixels' */ +int bitmask_overlap_area(const bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset); + +/* Fills a mask with the overlap of two other masks. A bitwise AND. */ +void bitmask_overlap_mask (const bitmask_t *a, const bitmask_t *b, bitmask_t *c, int xoffset, int yoffset); + +/* Draws mask b onto mask a (bitwise OR). Can be used to compose large + (game background?) mask from several submasks, which may speed up + the testing. */ + +void bitmask_draw(bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset); + +void bitmask_erase(bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset); + +/* Return a new scaled bitmask, with dimensions w*h. The quality of the + scaling may not be perfect for all circumstances, but it should + be reasonable. If either w or h is 0 a clear 1x1 mask is returned. */ +bitmask_t *bitmask_scale(const bitmask_t *m, int w, int h); + +/* Convolve b into a, drawing the output into o, shifted by offset. If offset + * is 0, then the (x,y) bit will be set if and only if + * bitmask_overlap(a, b, x - b->w - 1, y - b->h - 1) returns true. + * + * Modifies bits o[xoffset ... xoffset + a->w + b->w - 1) + * [yoffset ... yoffset + a->h + b->h - 1). */ +void bitmask_convolve(const bitmask_t *a, const bitmask_t *b, bitmask_t *o, int xoffset, int yoffset); + +#ifdef __cplusplus +} /* End of extern "C" { */ +#endif + +#endif diff --git a/Display/.venv/include/site/python3.7/pygame/include/pgcompat.h b/Display/.venv/include/site/python3.7/pygame/include/pgcompat.h new file mode 100644 index 0000000..2ba1b6b --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/include/pgcompat.h @@ -0,0 +1,180 @@ +/* Python 2.x/3.x and SDL compatibility tools + */ + +#if !defined(PGCOMPAT_H) +#define PGCOMPAT_H + +#include + +/* Cobjects vanish in Python 3.2; so we will code as though we use capsules */ +#if defined(Py_CAPSULE_H) +#define PG_HAVE_CAPSULE 1 +#else +#define PG_HAVE_CAPSULE 0 +#endif +#if defined(Py_COBJECT_H) +#define PG_HAVE_COBJECT 1 +#else +#define PG_HAVE_COBJECT 0 +#endif +#if !PG_HAVE_CAPSULE +#define PyCapsule_New(ptr, n, dfn) PyCObject_FromVoidPtr(ptr, dfn) +#define PyCapsule_GetPointer(obj, n) PyCObject_AsVoidPtr(obj) +#define PyCapsule_CheckExact(obj) PyCObject_Check(obj) +#endif + +/* Pygame uses Py_buffer (PEP 3118) to exchange array information internally; + * define here as needed. + */ +#if !defined(PyBUF_SIMPLE) +typedef struct bufferinfo { + void *buf; + PyObject *obj; + Py_ssize_t len; + Py_ssize_t itemsize; + int readonly; + int ndim; + char *format; + Py_ssize_t *shape; + Py_ssize_t *strides; + Py_ssize_t *suboffsets; + void *internal; +} Py_buffer; + +/* Flags for getting buffers */ +#define PyBUF_SIMPLE 0 +#define PyBUF_WRITABLE 0x0001 +/* we used to include an E, backwards compatible alias */ +#define PyBUF_WRITEABLE PyBUF_WRITABLE +#define PyBUF_FORMAT 0x0004 +#define PyBUF_ND 0x0008 +#define PyBUF_STRIDES (0x0010 | PyBUF_ND) +#define PyBUF_C_CONTIGUOUS (0x0020 | PyBUF_STRIDES) +#define PyBUF_F_CONTIGUOUS (0x0040 | PyBUF_STRIDES) +#define PyBUF_ANY_CONTIGUOUS (0x0080 | PyBUF_STRIDES) +#define PyBUF_INDIRECT (0x0100 | PyBUF_STRIDES) + +#define PyBUF_CONTIG (PyBUF_ND | PyBUF_WRITABLE) +#define PyBUF_CONTIG_RO (PyBUF_ND) + +#define PyBUF_STRIDED (PyBUF_STRIDES | PyBUF_WRITABLE) +#define PyBUF_STRIDED_RO (PyBUF_STRIDES) + +#define PyBUF_RECORDS (PyBUF_STRIDES | PyBUF_WRITABLE | PyBUF_FORMAT) +#define PyBUF_RECORDS_RO (PyBUF_STRIDES | PyBUF_FORMAT) + +#define PyBUF_FULL (PyBUF_INDIRECT | PyBUF_WRITABLE | PyBUF_FORMAT) +#define PyBUF_FULL_RO (PyBUF_INDIRECT | PyBUF_FORMAT) + +#define PyBUF_READ 0x100 +#define PyBUF_WRITE 0x200 +#define PyBUF_SHADOW 0x400 + +typedef int(*getbufferproc)(PyObject *, Py_buffer *, int); +typedef void(*releasebufferproc)(Py_buffer *); +#endif /* ~defined(PyBUF_SIMPLE) */ + +/* define common types where SDL is not included */ +#ifndef SDL_VERSION_ATLEAST +#ifdef _MSC_VER +typedef unsigned __int8 uint8_t; +typedef unsigned __int32 uint32_t; +#else +#include +#endif +typedef uint32_t Uint32; +typedef uint8_t Uint8; +#endif /* no SDL */ + + +#if defined(SDL_VERSION_ATLEAST) + +#ifndef SDL_WINDOW_VULKAN +#define SDL_WINDOW_VULKAN 0 +#endif + +#ifndef SDL_WINDOW_ALWAYS_ON_TOP +#define SDL_WINDOW_ALWAYS_ON_TOP 0 +#endif + +#ifndef SDL_WINDOW_SKIP_TASKBAR +#define SDL_WINDOW_SKIP_TASKBAR 0 +#endif + +#ifndef SDL_WINDOW_UTILITY +#define SDL_WINDOW_UTILITY 0 +#endif + +#ifndef SDL_WINDOW_TOOLTIP +#define SDL_WINDOW_TOOLTIP 0 +#endif + +#ifndef SDL_WINDOW_POPUP_MENU +#define SDL_WINDOW_POPUP_MENU 0 +#endif + + +#ifndef SDL_WINDOW_INPUT_GRABBED +#define SDL_WINDOW_INPUT_GRABBED 0 +#endif + +#ifndef SDL_WINDOW_INPUT_FOCUS +#define SDL_WINDOW_INPUT_FOCUS 0 +#endif + +#ifndef SDL_WINDOW_MOUSE_FOCUS +#define SDL_WINDOW_MOUSE_FOCUS 0 +#endif + +#ifndef SDL_WINDOW_FOREIGN +#define SDL_WINDOW_FOREIGN 0 +#endif + +#ifndef SDL_WINDOW_ALLOW_HIGHDPI +#define SDL_WINDOW_ALLOW_HIGHDPI 0 +#endif + +#ifndef SDL_WINDOW_MOUSE_CAPTURE +#define SDL_WINDOW_MOUSE_CAPTURE 0 +#endif + +#ifndef SDL_WINDOW_ALWAYS_ON_TOP +#define SDL_WINDOW_ALWAYS_ON_TOP 0 +#endif + +#ifndef SDL_WINDOW_SKIP_TASKBAR +#define SDL_WINDOW_SKIP_TASKBAR 0 +#endif + +#ifndef SDL_WINDOW_UTILITY +#define SDL_WINDOW_UTILITY 0 +#endif + +#ifndef SDL_WINDOW_TOOLTIP +#define SDL_WINDOW_TOOLTIP 0 +#endif + +#ifndef SDL_WINDOW_POPUP_MENU +#define SDL_WINDOW_POPUP_MENU 0 +#endif + +#if SDL_VERSION_ATLEAST(2, 0, 4) +/* To control the use of: + * SDL_AUDIODEVICEADDED + * SDL_AUDIODEVICEREMOVED + * + * Ref: https://wiki.libsdl.org/SDL_EventType + * Ref: https://wiki.libsdl.org/SDL_AudioDeviceEvent + */ +#define SDL2_AUDIODEVICE_SUPPORTED +#endif + +#ifndef SDL_MOUSEWHEEL_FLIPPED +#define NO_SDL_MOUSEWHEEL_FLIPPED +#endif + + +#endif /* defined(SDL_VERSION_ATLEAST) */ + + +#endif /* ~defined(PGCOMPAT_H) */ diff --git a/Display/.venv/include/site/python3.7/pygame/include/pgimport.h b/Display/.venv/include/site/python3.7/pygame/include/pgimport.h new file mode 100644 index 0000000..83e93aa --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/include/pgimport.h @@ -0,0 +1,80 @@ +#ifndef PGIMPORT_H +#define PGIMPORT_H + +/* Prefix when initializing module */ +#define MODPREFIX "" +/* Prefix when importing module */ +#define IMPPREFIX "pygame." + +#ifdef __SYMBIAN32__ + +/* On Symbian there is no pygame package. The extensions are built-in or in + * sys\bin. */ +#undef MODPREFIX +#undef IMPPREFIX +#define MODPREFIX "pygame_" +#define IMPPREFIX "pygame_" + +#endif /* __SYMBIAN32__ */ + +#include "pgcompat.h" + +#define PYGAMEAPI_LOCAL_ENTRY "_PYGAME_C_API" +#define PG_CAPSULE_NAME(m) (IMPPREFIX m "." PYGAMEAPI_LOCAL_ENTRY) + +/* + * fill API slots defined by PYGAMEAPI_DEFINE_SLOTS/PYGAMEAPI_EXTERN_SLOTS + */ +#define _IMPORT_PYGAME_MODULE(module) \ + { \ + PyObject *_module = PyImport_ImportModule(IMPPREFIX #module); \ + \ + if (_module != NULL) { \ + PyObject *_c_api = \ + PyObject_GetAttrString(_module, PYGAMEAPI_LOCAL_ENTRY); \ + \ + Py_DECREF(_module); \ + if (_c_api != NULL && PyCapsule_CheckExact(_c_api)) { \ + void **localptr = (void **)PyCapsule_GetPointer( \ + _c_api, PG_CAPSULE_NAME(#module)); \ + _PGSLOTS_ ## module = localptr; \ + } \ + Py_XDECREF(_c_api); \ + } \ + } + +#define PYGAMEAPI_IS_IMPORTED(module) (_PGSLOTS_ ## module != NULL) + +/* + * source file must include one of these in order to use _IMPORT_PYGAME_MODULE. + * this is set by import_pygame_*() functions. + * disable with NO_PYGAME_C_API + */ +#define PYGAMEAPI_DEFINE_SLOTS(module) \ + void ** _PGSLOTS_ ## module = NULL +#define PYGAMEAPI_EXTERN_SLOTS(module) \ + extern void **_PGSLOTS_ ## module +#define PYGAMEAPI_GET_SLOT(module, index) \ + _PGSLOTS_ ## module [(index)] + +/* + * disabled API with NO_PYGAME_C_API; do nothing instead + */ +#ifdef NO_PYGAME_C_API + +#undef PYGAMEAPI_DEFINE_SLOTS +#undef PYGAMEAPI_EXTERN_SLOTS + +#define PYGAMEAPI_DEFINE_SLOTS(module) +#define PYGAMEAPI_EXTERN_SLOTS(module) + +/* intentionally leave this defined to cause a compiler error * +#define PYGAMEAPI_GET_SLOT(api_root, index) +#undef PYGAMEAPI_GET_SLOT*/ + +#undef _IMPORT_PYGAME_MODULE +#define _IMPORT_PYGAME_MODULE(module) + +#endif /* NO_PYGAME_C_API */ + +#endif /* ~PGIMPORT_H */ diff --git a/Display/.venv/include/site/python3.7/pygame/include/pgplatform.h b/Display/.venv/include/site/python3.7/pygame/include/pgplatform.h new file mode 100644 index 0000000..2d1d5c8 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/include/pgplatform.h @@ -0,0 +1,45 @@ +/* platform/compiler adjustments */ +#ifndef PG_PLATFORM_H +#define PG_PLATFORM_H + +#if defined(HAVE_SNPRINTF) /* defined in python.h (pyerrors.h) and SDL.h \ + (SDL_config.h) */ +#undef HAVE_SNPRINTF /* remove GCC redefine warning */ +#endif /* HAVE_SNPRINTF */ + +#ifndef PG_INLINE +#if defined(__clang__) +#define PG_INLINE __inline__ __attribute__((__unused__)) +#elif defined(__GNUC__) +#define PG_INLINE __inline__ +#elif defined(_MSC_VER) +#define PG_INLINE __inline +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define PG_INLINE inline +#else +#define PG_INLINE +#endif +#endif /* ~PG_INLINE */ + +/* This is unconditionally defined in Python.h */ +#if defined(_POSIX_C_SOURCE) +#undef _POSIX_C_SOURCE +#endif + +/* No signal() */ +#if defined(__SYMBIAN32__) && defined(HAVE_SIGNAL_H) +#undef HAVE_SIGNAL_H +#endif + +#if defined(HAVE_SNPRINTF) +#undef HAVE_SNPRINTF +#endif + +/* SDL needs WIN32 */ +#if !defined(WIN32) && \ + (defined(MS_WIN32) || defined(_WIN32) || \ + defined(__WIN32) || defined(__WIN32__) || defined(_WINDOWS)) +#define WIN32 +#endif + +#endif /* ~PG_PLATFORM_H */ diff --git a/Display/.venv/include/site/python3.7/pygame/include/pygame.h b/Display/.venv/include/site/python3.7/pygame/include/pygame.h new file mode 100644 index 0000000..bcbf1d9 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/include/pygame.h @@ -0,0 +1,34 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +/* To allow the Pygame C api to be globally shared by all code within an + * extension module built from multiple C files, only include the pygame.h + * header within the top level C file, the one which calls the + * 'import_pygame_*' macros. All other C source files of the module should + * include _pygame.h instead. + */ +#ifndef PYGAME_H +#define PYGAME_H + +#include "_pygame.h" + +#endif diff --git a/Display/.venv/include/site/python3.7/pygame/include/pygame_bufferproxy.h b/Display/.venv/include/site/python3.7/pygame/include/pygame_bufferproxy.h new file mode 100644 index 0000000..d7c4ac6 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/include/pygame_bufferproxy.h @@ -0,0 +1,59 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + Copyright (C) 2007 Rene Dudfield, Richard Goedeken + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +/* Bufferproxy module C api. */ +#if !defined(PG_BUFPROXY_HEADER) +#define PG_BUFPROXY_HEADER + +#include + +typedef PyObject *(*_pgbufproxy_new_t)(PyObject *, getbufferproc); +typedef PyObject *(*_pgbufproxy_get_obj_t)(PyObject *); +typedef int (*_pgbufproxy_trip_t)(PyObject *); + +#ifndef PYGAMEAPI_BUFPROXY_INTERNAL + +#include "pgimport.h" + +PYGAMEAPI_DEFINE_SLOTS(bufferproxy); + +#define pgBufproxy_Type (*(PyTypeObject*) \ + PYGAMEAPI_GET_SLOT(bufferproxy, 0) ) + +#define pgBufproxy_Check(x) ((x)->ob_type == &pgBufproxy_Type) + +#define pgBufproxy_New (*(_pgbufproxy_new_t) \ + PYGAMEAPI_GET_SLOT(bufferproxy, 1)) + +#define pgBufproxy_GetParent \ + (*(_pgbufproxy_get_obj_t) \ + PYGAMEAPI_GET_SLOT(bufferproxy, 2)) + +#define pgBufproxy_Trip (*(_pgbufproxy_trip_t) \ + PYGAMEAPI_GET_SLOT(bufferproxy, 3)) + +#define import_pygame_bufferproxy() _IMPORT_PYGAME_MODULE(bufferproxy) + +#endif /* ~PYGAMEAPI_BUFPROXY_INTERNAL */ + +#endif /* ~defined(PG_BUFPROXY_HEADER) */ diff --git a/Display/.venv/include/site/python3.7/pygame/include/pygame_font.h b/Display/.venv/include/site/python3.7/pygame/include/pygame_font.h new file mode 100644 index 0000000..3fe4500 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/include/pygame_font.h @@ -0,0 +1,53 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +#include +#include "pgplatform.h" + +struct TTF_Font; + +typedef struct { + PyObject_HEAD + TTF_Font* font; + PyObject* weakreflist; +} PyFontObject; +#define PyFont_AsFont(x) (((PyFontObject*)x)->font) + +#ifndef PYGAMEAPI_FONT_INTERNAL + +#include "pgimport.h" + +PYGAMEAPI_DEFINE_SLOTS(font); + +#define PyFont_Type (*(PyTypeObject*) \ + PYGAMEAPI_GET_SLOT(font, 0)) +#define PyFont_Check(x) ((x)->ob_type == &PyFont_Type) + +#define PyFont_New (*(PyObject*(*)(TTF_Font*))\ + PYGAMEAPI_GET_SLOT(font, 1)) + +/*slot 2 taken by FONT_INIT_CHECK*/ + +#define import_pygame_font() _IMPORT_PYGAME_MODULE(font) + +#endif + diff --git a/Display/.venv/include/site/python3.7/pygame/include/pygame_freetype.h b/Display/.venv/include/site/python3.7/pygame/include/pygame_freetype.h new file mode 100644 index 0000000..f727ee2 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/include/pygame_freetype.h @@ -0,0 +1,43 @@ +/* + pygame - Python Game Library + Copyright (C) 2009 Vicent Marti + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#ifndef PYGAME_FREETYPE_H_ +#define PYGAME_FREETYPE_H_ + +#include "pgplatform.h" +#include "pgimport.h" +#include "pgcompat.h" + +#ifndef PYGAME_FREETYPE_INTERNAL + +PYGAMEAPI_DEFINE_SLOTS(_freetype); + +#define pgFont_Type (*(PyTypeObject*) \ + PYGAMEAPI_GET_SLOT(_freetype, 0)) + +#define pgFont_Check(x) ((x)->ob_type == &pgFont_Type) + +#define pgFont_New (*(PyObject*(*)(const char*, long)) \ + PYGAMEAPI_GET_SLOT(_freetype, 1)) + +#define import_pygame_freetype() _IMPORT_PYGAME_MODULE(_freetype) + +#endif /* PYGAME_FREETYPE_INTERNAL */ + +#endif /* PYGAME_FREETYPE_H_ */ diff --git a/Display/.venv/include/site/python3.7/pygame/include/pygame_mask.h b/Display/.venv/include/site/python3.7/pygame/include/pygame_mask.h new file mode 100644 index 0000000..1b25553 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/include/pygame_mask.h @@ -0,0 +1,47 @@ +/* + pygame - Python Game Library + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef PGMASK_H +#define PGMASK_H + +#include +#include "bitmask.h" + +typedef struct { + PyObject_HEAD + bitmask_t *mask; + void *bufdata; +} pgMaskObject; + +#define pgMask_AsBitmap(x) (((pgMaskObject*)x)->mask) + +#ifndef PYGAMEAPI_MASK_INTERNAL + +#include "pgimport.h" + +PYGAMEAPI_DEFINE_SLOTS(mask); + +#define pgMask_Type (*(PyTypeObject*) \ + PYGAMEAPI_GET_SLOT(mask, 0)) +#define pgMask_Check(x) ((x)->ob_type == &pgMask_Type) + +#define import_pygame_mask() _IMPORT_PYGAME_MODULE(mask) + +#endif /* ~PYGAMEAPI_MASK_INTERNAL */ + +#endif /* ~PGMASK_H */ diff --git a/Display/.venv/include/site/python3.7/pygame/include/pygame_mixer.h b/Display/.venv/include/site/python3.7/pygame/include/pygame_mixer.h new file mode 100644 index 0000000..52e76ae --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/include/pygame_mixer.h @@ -0,0 +1,82 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +#ifndef PGMIXER_H +#define PGMIXER_H + +#include +#include + +#include "pgcompat.h" + +struct Mix_Chunk; + +typedef struct { + PyObject_HEAD + Mix_Chunk *chunk; + Uint8 *mem; + PyObject *weakreflist; +} pgSoundObject; + +typedef struct { + PyObject_HEAD + int chan; +} pgChannelObject; + +#define pgSound_AsChunk(x) (((pgSoundObject*)x)->chunk) +#define pgChannel_AsInt(x) (((pgChannelObject*)x)->chan) + +#include "pgimport.h" + +#ifndef PYGAMEAPI_MIXER_INTERNAL + +PYGAMEAPI_DEFINE_SLOTS(mixer); + +#define pgSound_Type (*(PyTypeObject*) \ + PYGAMEAPI_GET_SLOT(mixer, 0)) + +#define pgSound_Check(x) ((x)->ob_type == &pgSound_Type) + +#define pgSound_New (*(PyObject*(*)(Mix_Chunk*)) \ + PYGAMEAPI_GET_SLOT(mixer, 1)) + +#define pgSound_Play (*(PyObject*(*)(PyObject*, PyObject*)) \ + PYGAMEAPI_GET_SLOT(mixer, 2)) + +#define pgChannel_Type (*(PyTypeObject*) \ + PYGAMEAPI_GET_SLOT(mixer, 3)) +#define pgChannel_Check(x) ((x)->ob_type == &pgChannel_Type) + +#define pgChannel_New (*(PyObject*(*)(int)) \ + PYGAMEAPI_GET_SLOT(mixer, 4)) + +#define pgMixer_AutoInit (*(PyObject*(*)(PyObject*, PyObject*)) \ + PYGAMEAPI_GET_SLOT(mixer, 5)) + +#define pgMixer_AutoQuit (*(void(*)(void)) \ + PYGAMEAPI_GET_SLOT(mixer, 6)) + +#define import_pygame_mixer() _IMPORT_PYGAME_MODULE(mixer) + +#endif /* PYGAMEAPI_MIXER_INTERNAL */ + +#endif /* ~PGMIXER_H */ diff --git a/Display/.venv/include/site/python3.7/pygame/mask.h b/Display/.venv/include/site/python3.7/pygame/mask.h new file mode 100644 index 0000000..45ad8c5 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/mask.h @@ -0,0 +1,7 @@ +#ifndef PGMASK_INTERNAL_H +#define PGMASK_INTERNAL_H + +#include "include/pygame_mask.h" +#define PYGAMEAPI_MASK_NUMSLOTS 1 + +#endif /* ~PGMASK_INTERNAL_H */ diff --git a/Display/.venv/include/site/python3.7/pygame/mixer.h b/Display/.venv/include/site/python3.7/pygame/mixer.h new file mode 100644 index 0000000..3650612 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/mixer.h @@ -0,0 +1,14 @@ +#ifndef MIXER_INTERNAL_H +#define MIXER_INTERNAL_H + +#include + +/* test mixer initializations */ +#define MIXER_INIT_CHECK() \ + if(!SDL_WasInit(SDL_INIT_AUDIO)) \ + return RAISE(pgExc_SDLError, "mixer not initialized") + +#define PYGAMEAPI_MIXER_NUMSLOTS 7 +#include "include/pygame_mixer.h" + +#endif /* ~MIXER_INTERNAL_H */ diff --git a/Display/.venv/include/site/python3.7/pygame/palette.h b/Display/.venv/include/site/python3.7/pygame/palette.h new file mode 100644 index 0000000..1ae4cf6 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/palette.h @@ -0,0 +1,123 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +#ifndef PALETTE_H +#define PALETTE_H + +#include + +/* SDL 2 does not assign a default palette color scheme to a new 8 bit + * surface. Instead, the palette is set all white. This defines the SDL 1.2 + * default palette. + */ +static const SDL_Color default_palette_colors[] = { + {0, 0, 0, 255}, {0, 0, 85, 255}, {0, 0, 170, 255}, + {0, 0, 255, 255}, {0, 36, 0, 255}, {0, 36, 85, 255}, + {0, 36, 170, 255}, {0, 36, 255, 255}, {0, 73, 0, 255}, + {0, 73, 85, 255}, {0, 73, 170, 255}, {0, 73, 255, 255}, + {0, 109, 0, 255}, {0, 109, 85, 255}, {0, 109, 170, 255}, + {0, 109, 255, 255}, {0, 146, 0, 255}, {0, 146, 85, 255}, + {0, 146, 170, 255}, {0, 146, 255, 255}, {0, 182, 0, 255}, + {0, 182, 85, 255}, {0, 182, 170, 255}, {0, 182, 255, 255}, + {0, 219, 0, 255}, {0, 219, 85, 255}, {0, 219, 170, 255}, + {0, 219, 255, 255}, {0, 255, 0, 255}, {0, 255, 85, 255}, + {0, 255, 170, 255}, {0, 255, 255, 255}, {85, 0, 0, 255}, + {85, 0, 85, 255}, {85, 0, 170, 255}, {85, 0, 255, 255}, + {85, 36, 0, 255}, {85, 36, 85, 255}, {85, 36, 170, 255}, + {85, 36, 255, 255}, {85, 73, 0, 255}, {85, 73, 85, 255}, + {85, 73, 170, 255}, {85, 73, 255, 255}, {85, 109, 0, 255}, + {85, 109, 85, 255}, {85, 109, 170, 255}, {85, 109, 255, 255}, + {85, 146, 0, 255}, {85, 146, 85, 255}, {85, 146, 170, 255}, + {85, 146, 255, 255}, {85, 182, 0, 255}, {85, 182, 85, 255}, + {85, 182, 170, 255}, {85, 182, 255, 255}, {85, 219, 0, 255}, + {85, 219, 85, 255}, {85, 219, 170, 255}, {85, 219, 255, 255}, + {85, 255, 0, 255}, {85, 255, 85, 255}, {85, 255, 170, 255}, + {85, 255, 255, 255}, {170, 0, 0, 255}, {170, 0, 85, 255}, + {170, 0, 170, 255}, {170, 0, 255, 255}, {170, 36, 0, 255}, + {170, 36, 85, 255}, {170, 36, 170, 255}, {170, 36, 255, 255}, + {170, 73, 0, 255}, {170, 73, 85, 255}, {170, 73, 170, 255}, + {170, 73, 255, 255}, {170, 109, 0, 255}, {170, 109, 85, 255}, + {170, 109, 170, 255}, {170, 109, 255, 255}, {170, 146, 0, 255}, + {170, 146, 85, 255}, {170, 146, 170, 255}, {170, 146, 255, 255}, + {170, 182, 0, 255}, {170, 182, 85, 255}, {170, 182, 170, 255}, + {170, 182, 255, 255}, {170, 219, 0, 255}, {170, 219, 85, 255}, + {170, 219, 170, 255}, {170, 219, 255, 255}, {170, 255, 0, 255}, + {170, 255, 85, 255}, {170, 255, 170, 255}, {170, 255, 255, 255}, + {255, 0, 0, 255}, {255, 0, 85, 255}, {255, 0, 170, 255}, + {255, 0, 255, 255}, {255, 36, 0, 255}, {255, 36, 85, 255}, + {255, 36, 170, 255}, {255, 36, 255, 255}, {255, 73, 0, 255}, + {255, 73, 85, 255}, {255, 73, 170, 255}, {255, 73, 255, 255}, + {255, 109, 0, 255}, {255, 109, 85, 255}, {255, 109, 170, 255}, + {255, 109, 255, 255}, {255, 146, 0, 255}, {255, 146, 85, 255}, + {255, 146, 170, 255}, {255, 146, 255, 255}, {255, 182, 0, 255}, + {255, 182, 85, 255}, {255, 182, 170, 255}, {255, 182, 255, 255}, + {255, 219, 0, 255}, {255, 219, 85, 255}, {255, 219, 170, 255}, + {255, 219, 255, 255}, {255, 255, 0, 255}, {255, 255, 85, 255}, + {255, 255, 170, 255}, {255, 255, 255, 255}, {0, 0, 0, 255}, + {0, 0, 85, 255}, {0, 0, 170, 255}, {0, 0, 255, 255}, + {0, 36, 0, 255}, {0, 36, 85, 255}, {0, 36, 170, 255}, + {0, 36, 255, 255}, {0, 73, 0, 255}, {0, 73, 85, 255}, + {0, 73, 170, 255}, {0, 73, 255, 255}, {0, 109, 0, 255}, + {0, 109, 85, 255}, {0, 109, 170, 255}, {0, 109, 255, 255}, + {0, 146, 0, 255}, {0, 146, 85, 255}, {0, 146, 170, 255}, + {0, 146, 255, 255}, {0, 182, 0, 255}, {0, 182, 85, 255}, + {0, 182, 170, 255}, {0, 182, 255, 255}, {0, 219, 0, 255}, + {0, 219, 85, 255}, {0, 219, 170, 255}, {0, 219, 255, 255}, + {0, 255, 0, 255}, {0, 255, 85, 255}, {0, 255, 170, 255}, + {0, 255, 255, 255}, {85, 0, 0, 255}, {85, 0, 85, 255}, + {85, 0, 170, 255}, {85, 0, 255, 255}, {85, 36, 0, 255}, + {85, 36, 85, 255}, {85, 36, 170, 255}, {85, 36, 255, 255}, + {85, 73, 0, 255}, {85, 73, 85, 255}, {85, 73, 170, 255}, + {85, 73, 255, 255}, {85, 109, 0, 255}, {85, 109, 85, 255}, + {85, 109, 170, 255}, {85, 109, 255, 255}, {85, 146, 0, 255}, + {85, 146, 85, 255}, {85, 146, 170, 255}, {85, 146, 255, 255}, + {85, 182, 0, 255}, {85, 182, 85, 255}, {85, 182, 170, 255}, + {85, 182, 255, 255}, {85, 219, 0, 255}, {85, 219, 85, 255}, + {85, 219, 170, 255}, {85, 219, 255, 255}, {85, 255, 0, 255}, + {85, 255, 85, 255}, {85, 255, 170, 255}, {85, 255, 255, 255}, + {170, 0, 0, 255}, {170, 0, 85, 255}, {170, 0, 170, 255}, + {170, 0, 255, 255}, {170, 36, 0, 255}, {170, 36, 85, 255}, + {170, 36, 170, 255}, {170, 36, 255, 255}, {170, 73, 0, 255}, + {170, 73, 85, 255}, {170, 73, 170, 255}, {170, 73, 255, 255}, + {170, 109, 0, 255}, {170, 109, 85, 255}, {170, 109, 170, 255}, + {170, 109, 255, 255}, {170, 146, 0, 255}, {170, 146, 85, 255}, + {170, 146, 170, 255}, {170, 146, 255, 255}, {170, 182, 0, 255}, + {170, 182, 85, 255}, {170, 182, 170, 255}, {170, 182, 255, 255}, + {170, 219, 0, 255}, {170, 219, 85, 255}, {170, 219, 170, 255}, + {170, 219, 255, 255}, {170, 255, 0, 255}, {170, 255, 85, 255}, + {170, 255, 170, 255}, {170, 255, 255, 255}, {255, 0, 0, 255}, + {255, 0, 85, 255}, {255, 0, 170, 255}, {255, 0, 255, 255}, + {255, 36, 0, 255}, {255, 36, 85, 255}, {255, 36, 170, 255}, + {255, 36, 255, 255}, {255, 73, 0, 255}, {255, 73, 85, 255}, + {255, 73, 170, 255}, {255, 73, 255, 255}, {255, 109, 0, 255}, + {255, 109, 85, 255}, {255, 109, 170, 255}, {255, 109, 255, 255}, + {255, 146, 0, 255}, {255, 146, 85, 255}, {255, 146, 170, 255}, + {255, 146, 255, 255}, {255, 182, 0, 255}, {255, 182, 85, 255}, + {255, 182, 170, 255}, {255, 182, 255, 255}, {255, 219, 0, 255}, + {255, 219, 85, 255}, {255, 219, 170, 255}, {255, 219, 255, 255}, + {255, 255, 0, 255}, {255, 255, 85, 255}, {255, 255, 170, 255}, + {255, 255, 255, 255}}; + +static const int default_palette_size = + (int)(sizeof(default_palette_colors) / sizeof(SDL_Color)); + +#endif diff --git a/Display/.venv/include/site/python3.7/pygame/pgarrinter.h b/Display/.venv/include/site/python3.7/pygame/pgarrinter.h new file mode 100644 index 0000000..5ba096b --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/pgarrinter.h @@ -0,0 +1,26 @@ +/* array structure interface version 3 declarations */ + +#if !defined(PG_ARRAYINTER_HEADER) +#define PG_ARRAYINTER_HEADER + +static const int PAI_CONTIGUOUS = 0x01; +static const int PAI_FORTRAN = 0x02; +static const int PAI_ALIGNED = 0x100; +static const int PAI_NOTSWAPPED = 0x200; +static const int PAI_WRITEABLE = 0x400; +static const int PAI_ARR_HAS_DESCR = 0x800; + +typedef struct { + int two; /* contains the integer 2 -- simple sanity check */ + int nd; /* number of dimensions */ + char typekind; /* kind in array -- character code of typestr */ + int itemsize; /* size of each element */ + int flags; /* flags indicating how the data should be */ + /* interpreted */ + Py_intptr_t *shape; /* A length-nd array of shape information */ + Py_intptr_t *strides; /* A length-nd array of stride information */ + void *data; /* A pointer to the first element of the array */ + PyObject *descr; /* NULL or a data-description */ +} PyArrayInterface; + +#endif diff --git a/Display/.venv/include/site/python3.7/pygame/pgbufferproxy.h b/Display/.venv/include/site/python3.7/pygame/pgbufferproxy.h new file mode 100644 index 0000000..1507608 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/pgbufferproxy.h @@ -0,0 +1,7 @@ +#ifndef PG_BUFPROXY_INTERNAL_H +#define PG_BUFPROXY_INTERNAL_H + +#include "include/pygame_bufferproxy.h" +#define PYGAMEAPI_BUFPROXY_NUMSLOTS 4 + +#endif /* ~PG_BUFPROXY_INTERNAL_H */ diff --git a/Display/.venv/include/site/python3.7/pygame/pgcompat.h b/Display/.venv/include/site/python3.7/pygame/pgcompat.h new file mode 100644 index 0000000..a8d2949 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/pgcompat.h @@ -0,0 +1,214 @@ +/* Python 2.x/3.x compatibility tools (internal) + */ +#ifndef PGCOMPAT_INTERNAL_H +#define PGCOMPAT_INTERNAL_H + +#include "include/pgcompat.h" + +#if PY_MAJOR_VERSION >= 3 + +#define PY3 1 + +/* Define some aliases for the removed PyInt_* functions */ +#define PyInt_Check(op) PyLong_Check(op) +#define PyInt_FromString PyLong_FromString +#define PyInt_FromUnicode PyLong_FromUnicode +#define PyInt_FromLong PyLong_FromLong +#define PyInt_FromSize_t PyLong_FromSize_t +#define PyInt_FromSsize_t PyLong_FromSsize_t +#define PyInt_AsLong PyLong_AsLong +#define PyInt_AsSsize_t PyLong_AsSsize_t +#define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask +#define PyInt_AsUnsignedLongLongMask PyLong_AsUnsignedLongLongMask +#define PyInt_AS_LONG PyLong_AS_LONG +#define PyNumber_Int PyNumber_Long + +/* Weakrefs flags changed in 3.x */ +#define Py_TPFLAGS_HAVE_WEAKREFS 0 + +/* Module init function returns new module instance. */ +#define MODINIT_RETURN(x) return x +#define MODINIT_DEFINE(mod_name) PyMODINIT_FUNC PyInit_##mod_name (void) +#define DECREF_MOD(mod) Py_DECREF (mod) + +/* Type header differs. */ +#define TYPE_HEAD(x,y) PyVarObject_HEAD_INIT(x,y) + +/* Text interface. Use unicode strings. */ +#define Text_Type PyUnicode_Type +#define Text_Check PyUnicode_Check + +#ifndef PYPY_VERSION +#define Text_FromLocale(s) PyUnicode_DecodeLocale((s), "strict") +#else /* PYPY_VERSION */ +/* workaround: missing function for pypy */ +#define Text_FromLocale PyUnicode_FromString +#endif /* PYPY_VERSION */ + +#define Text_FromUTF8 PyUnicode_FromString +#define Text_FromUTF8AndSize PyUnicode_FromStringAndSize +#define Text_FromFormat PyUnicode_FromFormat +#define Text_GetSize PyUnicode_GetSize +#define Text_GET_SIZE PyUnicode_GET_SIZE + +/* Binary interface. Use bytes. */ +#define Bytes_Type PyBytes_Type +#define Bytes_Check PyBytes_Check +#define Bytes_Size PyBytes_Size +#define Bytes_AsString PyBytes_AsString +#define Bytes_AsStringAndSize PyBytes_AsStringAndSize +#define Bytes_FromStringAndSize PyBytes_FromStringAndSize +#define Bytes_FromFormat PyBytes_FromFormat +#define Bytes_AS_STRING PyBytes_AS_STRING +#define Bytes_GET_SIZE PyBytes_GET_SIZE +#define Bytes_AsDecodeObject PyBytes_AsDecodedObject + +#define Object_Unicode PyObject_Str + +#define IsTextObj(x) (PyUnicode_Check(x) || PyBytes_Check(x)) + +/* Renamed builtins */ +#define BUILTINS_MODULE "builtins" +#define BUILTINS_UNICODE "str" +#define BUILTINS_UNICHR "chr" + +/* Defaults for unicode file path encoding */ +#define UNICODE_DEF_FS_CODEC Py_FileSystemDefaultEncoding +#if defined(MS_WIN32) +#define UNICODE_DEF_FS_ERROR "replace" +#else +#define UNICODE_DEF_FS_ERROR "surrogateescape" +#endif + +#else /* #if PY_MAJOR_VERSION >= 3 */ + +#define PY3 0 + +/* Module init function returns nothing. */ +#define MODINIT_RETURN(x) return +#define MODINIT_DEFINE(mod_name) PyMODINIT_FUNC init##mod_name (void) +#define DECREF_MOD(mod) + +/* Type header differs. */ +#define TYPE_HEAD(x,y) \ + PyObject_HEAD_INIT(x) \ + 0, + +/* Text interface. Use ascii strings. */ +#define Text_Type PyString_Type +#define Text_Check PyString_Check +#define Text_FromLocale PyString_FromString +#define Text_FromUTF8 PyString_FromString +#define Text_FromUTF8AndSize PyString_FromStringAndSize +#define Text_FromFormat PyString_FromFormat +#define Text_GetSize PyString_GetSize +#define Text_GET_SIZE PyString_GET_SIZE + +/* Binary interface. Use ascii strings. */ +#define Bytes_Type PyString_Type +#define Bytes_Check PyString_Check +#define Bytes_Size PyString_Size +#define Bytes_AsString PyString_AsString +#define Bytes_AsStringAndSize PyString_AsStringAndSize +#define Bytes_FromStringAndSize PyString_FromStringAndSize +#define Bytes_FromFormat PyString_FromFormat +#define Bytes_AS_STRING PyString_AS_STRING +#define Bytes_GET_SIZE PyString_GET_SIZE +#define Bytes_AsDecodedObject PyString_AsDecodedObject + +#define Object_Unicode PyObject_Unicode + +/* Renamed builtins */ +#define BUILTINS_MODULE "__builtin__" +#define BUILTINS_UNICODE "unicode" +#define BUILTINS_UNICHR "unichr" + +/* Defaults for unicode file path encoding */ +#define UNICODE_DEF_FS_CODEC Py_FileSystemDefaultEncoding +#define UNICODE_DEF_FS_ERROR "strict" + +#endif /* #if PY_MAJOR_VERSION >= 3 */ + +#define PY2 (!PY3) + +#define MODINIT_ERROR MODINIT_RETURN (NULL) + +/* Module state. These macros are used to define per-module macros. + * v - global state variable (Python 2.x) + * s - global state structure (Python 3.x) + */ +#define PY2_GETSTATE(v) (&(v)) +#define PY3_GETSTATE(s, m) ((struct s *) PyModule_GetState (m)) + +/* Pep 3123: Making PyObject_HEAD conform to standard C */ +#if !defined(Py_TYPE) +#define Py_TYPE(o) (((PyObject *)(o))->ob_type) +#define Py_REFCNT(o) (((PyObject *)(o))->ob_refcnt) +#define Py_SIZE(o) (((PyVarObject *)(o))->ob_size) +#endif + +/* Encode a unicode file path */ +#define Unicode_AsEncodedPath(u) \ + PyUnicode_AsEncodedString ((u), UNICODE_DEF_FS_CODEC, UNICODE_DEF_FS_ERROR) + +#define RELATIVE_MODULE(m) ("." m) + +#define HAVE_OLD_BUFPROTO PY2 + +#if !defined(PG_ENABLE_OLDBUF) /* allow for command line override */ +#if HAVE_OLD_BUFPROTO +#define PG_ENABLE_OLDBUF 1 +#else +#define PG_ENABLE_OLDBUF 0 +#endif +#endif + +#ifndef Py_TPFLAGS_HAVE_NEWBUFFER +#define Py_TPFLAGS_HAVE_NEWBUFFER 0 +#endif + +#ifndef Py_TPFLAGS_HAVE_CLASS +#define Py_TPFLAGS_HAVE_CLASS 0 +#endif + +#ifndef Py_TPFLAGS_CHECKTYPES +#define Py_TPFLAGS_CHECKTYPES 0 +#endif + +#if PY_VERSION_HEX >= 0x03020000 +#define Slice_GET_INDICES_EX(slice, length, start, stop, step, slicelength) \ + PySlice_GetIndicesEx(slice, length, start, stop, step, slicelength) +#else +#define Slice_GET_INDICES_EX(slice, length, start, stop, step, slicelength) \ + PySlice_GetIndicesEx((PySliceObject *)(slice), length, \ + start, stop, step, slicelength) +#endif + +/* Support new buffer protocol? */ +#if !defined(PG_ENABLE_NEWBUF) /* allow for command line override */ +#if !defined(PYPY_VERSION) +#define PG_ENABLE_NEWBUF 1 +#else +#define PG_ENABLE_NEWBUF 0 +#endif +#endif /* !defined(PG_ENABLE_NEWBUF) */ + + +#if defined(SDL_VERSION_ATLEAST) +#if !(SDL_VERSION_ATLEAST(2, 0, 5)) +/* These functions require SDL 2.0.5 or greater. + + https://wiki.libsdl.org/SDL_SetWindowResizable +*/ +void SDL_SetWindowResizable(SDL_Window *window, SDL_bool resizable); +int SDL_GetWindowOpacity(SDL_Window *window, float *opacity); +int SDL_SetWindowOpacity(SDL_Window *window, float opacity); +int SDL_SetWindowModalFor(SDL_Window *modal_window, SDL_Window *parent_window); +int SDL_SetWindowInputFocus(SDL_Window *window); +SDL_Surface * SDL_CreateRGBSurfaceWithFormat(Uint32 flags, int width, int height, int depth, + Uint32 format); +#endif +#endif /* defined(SDL_VERSION_ATLEAST) */ + + +#endif /* ~PGCOMPAT_INTERNAL_H */ diff --git a/Display/.venv/include/site/python3.7/pygame/pgimport.h b/Display/.venv/include/site/python3.7/pygame/pgimport.h new file mode 100644 index 0000000..34654c9 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/pgimport.h @@ -0,0 +1,13 @@ +#ifndef PGIMPORT_INTERNAL_H +#define PGIMPORT_INTERNAL_H + +#include "include/pgimport.h" + +#if PG_HAVE_CAPSULE +#define encapsulate_api(ptr, module) \ + PyCapsule_New(ptr, PG_CAPSULE_NAME(module), NULL) +#else /* ~PG_HAVE_CAPSULE */ +#define encapsulate_api(ptr, module) PyCObject_FromVoidPtr(ptr, NULL) +#endif /* ~PG_HAVE_CAPSULE */ + +#endif /* PGIMPORT_INTERNAL_H */ diff --git a/Display/.venv/include/site/python3.7/pygame/pgopengl.h b/Display/.venv/include/site/python3.7/pygame/pgopengl.h new file mode 100644 index 0000000..6440f32 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/pgopengl.h @@ -0,0 +1,18 @@ +#if !defined(PGOPENGL_H) +#define PGOPENGL_H + +/** This header includes definitions of Opengl functions as pointer types for + ** use with the SDL function SDL_GL_GetProcAddress. + **/ + +#if defined(_WIN32) +#define GL_APIENTRY __stdcall +#else +#define GL_APIENTRY +#endif + +typedef void (GL_APIENTRY *GL_glReadPixels_Func)(int, int, int, int, unsigned int, unsigned int, void*); + +typedef void (GL_APIENTRY *GL_glViewport_Func)(int, int, unsigned int, unsigned int); +#endif + diff --git a/Display/.venv/include/site/python3.7/pygame/pgplatform.h b/Display/.venv/include/site/python3.7/pygame/pgplatform.h new file mode 100644 index 0000000..2d5ed86 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/pgplatform.h @@ -0,0 +1,38 @@ +/* platform/compiler adjustments (internal) */ +#ifndef PG_PLATFORM_INTERNAL_H +#define PG_PLATFORM_INTERNAL_H + +/* This must be before all else */ +#if defined(__SYMBIAN32__) && defined(OPENC) +#include +#if defined(__WINS__) +void * +_alloca(size_t size); +#define alloca _alloca +#endif /* __WINS__ */ +#endif /* defined(__SYMBIAN32__) && defined(OPENC) */ + +#include "include/pgplatform.h" + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef ABS +#define ABS(a) (((a) < 0) ? -(a) : (a)) +#endif + +#if defined(macintosh) && defined(__MWERKS__) || defined(__SYMBIAN32__) +#define PYGAME_EXPORT __declspec(export) +#else +#define PYGAME_EXPORT +#endif + +/* warnings */ +#define PG_STRINGIZE_HELPER(x) #x +#define PG_STRINGIZE(x) PG_STRINGIZE_HELPER(x) +#define PG_WARN(desc) message(__FILE__ "(" PG_STRINGIZE(__LINE__) "): WARNING: " #desc) + +#endif /* ~PG_PLATFORM_INTERNAL_H */ diff --git a/Display/.venv/include/site/python3.7/pygame/pygame.h b/Display/.venv/include/site/python3.7/pygame/pygame.h new file mode 100644 index 0000000..d7eaf73 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/pygame.h @@ -0,0 +1,32 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +/* This will use PYGAMEAPI_DEFINE_SLOTS instead + * of PYGAMEAPI_EXTERN_SLOTS for base modules. + */ +#ifndef PYGAME_INTERNAL_H +#define PYGAME_INTERNAL_H + +#define PYGAME_H +#include "_pygame.h" + +#endif /* ~PYGAME_INTERNAL_H */ diff --git a/Display/.venv/include/site/python3.7/pygame/scrap.h b/Display/.venv/include/site/python3.7/pygame/scrap.h new file mode 100644 index 0000000..725f22f --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/scrap.h @@ -0,0 +1,148 @@ +/* + pygame - Python Game Library + Copyright (C) 2006, 2007 Rene Dudfield, Marcus von Appen + + Originally put in the public domain by Sam Lantinga. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef SCRAP_H +#define SCRAP_H + +/* This is unconditionally defined in Python.h */ +#if defined(_POSIX_C_SOURCE) +#undef _POSIX_C_SOURCE +#endif + +#include + +/* Handle clipboard text and data in arbitrary formats */ + +/** + * Predefined supported pygame scrap types. + */ +#define PYGAME_SCRAP_TEXT "text/plain" +#define PYGAME_SCRAP_BMP "image/bmp" +#define PYGAME_SCRAP_PPM "image/ppm" +#define PYGAME_SCRAP_PBM "image/pbm" + +/** + * The supported scrap clipboard types. + * + * This is only relevant in a X11 environment, which supports mouse + * selections as well. For Win32 and MacOS environments the default + * clipboard is used, no matter what value is passed. + */ +typedef enum +{ + SCRAP_CLIPBOARD, + SCRAP_SELECTION /* only supported in X11 environments. */ +} ScrapClipType; + +/** + * Macro for initialization checks. + */ +#define PYGAME_SCRAP_INIT_CHECK() \ + if(!pygame_scrap_initialized()) \ + return (PyErr_SetString (pgExc_SDLError, \ + "scrap system not initialized."), NULL) + +/** + * \brief Checks, whether the pygame scrap module was initialized. + * + * \return 1 if the modules was initialized, 0 otherwise. + */ +extern int +pygame_scrap_initialized (void); + +/** + * \brief Initializes the pygame scrap module internals. Call this before any + * other method. + * + * \return 1 on successful initialization, 0 otherwise. + */ +extern int +pygame_scrap_init (void); + +/** + * \brief Checks, whether the pygame window lost the clipboard focus or not. + * + * \return 1 if the window lost the focus, 0 otherwise. + */ +extern int +pygame_scrap_lost (void); + +/** + * \brief Places content of a specific type into the clipboard. + * + * \note For X11 the following notes are important: The following types + * are reserved for internal usage and thus will throw an error on + * setting them: "TIMESTAMP", "TARGETS", "SDL_SELECTION". + * Setting PYGAME_SCRAP_TEXT ("text/plain") will also automatically + * set the X11 types "STRING" (XA_STRING), "TEXT" and "UTF8_STRING". + * + * For Win32 the following notes are important: Setting + * PYGAME_SCRAP_TEXT ("text/plain") will also automatically set + * the Win32 type "TEXT" (CF_TEXT). + * + * For QNX the following notes are important: Setting + * PYGAME_SCRAP_TEXT ("text/plain") will also automatically set + * the QNX type "TEXT" (Ph_CL_TEXT). + * + * \param type The type of the content. + * \param srclen The length of the content. + * \param src The NULL terminated content. + * \return 1, if the content could be successfully pasted into the clipboard, + * 0 otherwise. + */ +extern int +pygame_scrap_put (char *type, int srclen, char *src); + +/** + * \brief Gets the current content from the clipboard. + * + * \note The received content does not need to be the content previously + * placed in the clipboard using pygame_put_scrap(). See the + * pygame_put_scrap() notes for more details. + * + * \param type The type of the content to receive. + * \param count The size of the returned content. + * \return The content or NULL in case of an error or if no content of the + * specified type was available. + */ +extern char* +pygame_scrap_get (char *type, unsigned long *count); + +/** + * \brief Gets the currently available content types from the clipboard. + * + * \return The different available content types or NULL in case of an + * error or if no content type is available. + */ +extern char** +pygame_scrap_get_types (void); + +/** + * \brief Checks whether content for the specified scrap type is currently + * available in the clipboard. + * + * \param type The type to check for. + * \return 1, if there is content and 0 otherwise. + */ +extern int +pygame_scrap_contains (char *type); + +#endif /* SCRAP_H */ diff --git a/Display/.venv/include/site/python3.7/pygame/surface.h b/Display/.venv/include/site/python3.7/pygame/surface.h new file mode 100644 index 0000000..cc5f071 --- /dev/null +++ b/Display/.venv/include/site/python3.7/pygame/surface.h @@ -0,0 +1,383 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + Copyright (C) 2007 Marcus von Appen + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +#ifndef SURFACE_H +#define SURFACE_H + +/* This is defined in SDL.h */ +#if defined(_POSIX_C_SOURCE) +#undef _POSIX_C_SOURCE +#endif + +#include +#include "pygame.h" + +/* Blend modes */ +#define PYGAME_BLEND_ADD 0x1 +#define PYGAME_BLEND_SUB 0x2 +#define PYGAME_BLEND_MULT 0x3 +#define PYGAME_BLEND_MIN 0x4 +#define PYGAME_BLEND_MAX 0x5 + +#define PYGAME_BLEND_RGB_ADD 0x1 +#define PYGAME_BLEND_RGB_SUB 0x2 +#define PYGAME_BLEND_RGB_MULT 0x3 +#define PYGAME_BLEND_RGB_MIN 0x4 +#define PYGAME_BLEND_RGB_MAX 0x5 + +#define PYGAME_BLEND_RGBA_ADD 0x6 +#define PYGAME_BLEND_RGBA_SUB 0x7 +#define PYGAME_BLEND_RGBA_MULT 0x8 +#define PYGAME_BLEND_RGBA_MIN 0x9 +#define PYGAME_BLEND_RGBA_MAX 0x10 +#define PYGAME_BLEND_PREMULTIPLIED 0x11 + + + + + +#if SDL_BYTEORDER == SDL_LIL_ENDIAN +#define GET_PIXEL_24(b) (b[0] + (b[1] << 8) + (b[2] << 16)) +#else +#define GET_PIXEL_24(b) (b[2] + (b[1] << 8) + (b[0] << 16)) +#endif + +#define GET_PIXEL(pxl, bpp, source) \ + switch (bpp) \ + { \ + case 2: \ + pxl = *((Uint16 *) (source)); \ + break; \ + case 4: \ + pxl = *((Uint32 *) (source)); \ + break; \ + default: \ + { \ + Uint8 *b = (Uint8 *) source; \ + pxl = GET_PIXEL_24(b); \ + } \ + break; \ + } + +#if IS_SDLv1 +#define GET_PIXELVALS(_sR, _sG, _sB, _sA, px, fmt, ppa) \ + _sR = ((px & fmt->Rmask) >> fmt->Rshift); \ + _sR = (_sR << fmt->Rloss) + (_sR >> (8 - (fmt->Rloss << 1))); \ + _sG = ((px & fmt->Gmask) >> fmt->Gshift); \ + _sG = (_sG << fmt->Gloss) + (_sG >> (8 - (fmt->Gloss << 1))); \ + _sB = ((px & fmt->Bmask) >> fmt->Bshift); \ + _sB = (_sB << fmt->Bloss) + (_sB >> (8 - (fmt->Bloss << 1))); \ + if (ppa) \ + { \ + _sA = ((px & fmt->Amask) >> fmt->Ashift); \ + _sA = (_sA << fmt->Aloss) + (_sA >> (8 - (fmt->Aloss << 1))); \ + } \ + else \ + { \ + _sA = 255; \ + } + +#define GET_PIXELVALS_1(sr, sg, sb, sa, _src, _fmt) \ + sr = _fmt->palette->colors[*((Uint8 *) (_src))].r; \ + sg = _fmt->palette->colors[*((Uint8 *) (_src))].g; \ + sb = _fmt->palette->colors[*((Uint8 *) (_src))].b; \ + sa = 255; + +/* For 1 byte palette pixels */ +#define SET_PIXELVAL(px, fmt, _dR, _dG, _dB, _dA) \ + *(px) = (Uint8) SDL_MapRGB(fmt, _dR, _dG, _dB) +#else /* IS_SDLv2 */ +#define GET_PIXELVALS(_sR, _sG, _sB, _sA, px, fmt, ppa) \ + SDL_GetRGBA(px, fmt, &(_sR), &(_sG), &(_sB), &(_sA)); \ + if (!ppa) { \ + _sA = 255; \ + } + +#define GET_PIXELVALS_1(sr, sg, sb, sa, _src, _fmt) \ + sr = _fmt->palette->colors[*((Uint8 *) (_src))].r; \ + sg = _fmt->palette->colors[*((Uint8 *) (_src))].g; \ + sb = _fmt->palette->colors[*((Uint8 *) (_src))].b; \ + sa = 255; + +/* For 1 byte palette pixels */ +#define SET_PIXELVAL(px, fmt, _dR, _dG, _dB, _dA) \ + *(px) = (Uint8) SDL_MapRGBA(fmt, _dR, _dG, _dB, _dA) +#endif /* IS_SDLv2 */ + + + + + + + + +#if SDL_BYTEORDER == SDL_LIL_ENDIAN +#define SET_OFFSETS_24(or, og, ob, fmt) \ + { \ + or = (fmt->Rshift == 0 ? 0 : \ + fmt->Rshift == 8 ? 1 : \ + 2 ); \ + og = (fmt->Gshift == 0 ? 0 : \ + fmt->Gshift == 8 ? 1 : \ + 2 ); \ + ob = (fmt->Bshift == 0 ? 0 : \ + fmt->Bshift == 8 ? 1 : \ + 2 ); \ + } + +#define SET_OFFSETS_32(or, og, ob, fmt) \ + { \ + or = (fmt->Rshift == 0 ? 0 : \ + fmt->Rshift == 8 ? 1 : \ + fmt->Rshift == 16 ? 2 : \ + 3 ); \ + og = (fmt->Gshift == 0 ? 0 : \ + fmt->Gshift == 8 ? 1 : \ + fmt->Gshift == 16 ? 2 : \ + 3 ); \ + ob = (fmt->Bshift == 0 ? 0 : \ + fmt->Bshift == 8 ? 1 : \ + fmt->Bshift == 16 ? 2 : \ + 3 ); \ + } +#else +#define SET_OFFSETS_24(or, og, ob, fmt) \ + { \ + or = (fmt->Rshift == 0 ? 2 : \ + fmt->Rshift == 8 ? 1 : \ + 0 ); \ + og = (fmt->Gshift == 0 ? 2 : \ + fmt->Gshift == 8 ? 1 : \ + 0 ); \ + ob = (fmt->Bshift == 0 ? 2 : \ + fmt->Bshift == 8 ? 1 : \ + 0 ); \ + } + +#define SET_OFFSETS_32(or, og, ob, fmt) \ + { \ + or = (fmt->Rshift == 0 ? 3 : \ + fmt->Rshift == 8 ? 2 : \ + fmt->Rshift == 16 ? 1 : \ + 0 ); \ + og = (fmt->Gshift == 0 ? 3 : \ + fmt->Gshift == 8 ? 2 : \ + fmt->Gshift == 16 ? 1 : \ + 0 ); \ + ob = (fmt->Bshift == 0 ? 3 : \ + fmt->Bshift == 8 ? 2 : \ + fmt->Bshift == 16 ? 1 : \ + 0 ); \ + } +#endif + + +#define CREATE_PIXEL(buf, r, g, b, a, bp, ft) \ + switch (bp) \ + { \ + case 2: \ + *((Uint16 *) (buf)) = \ + ((r >> ft->Rloss) << ft->Rshift) | \ + ((g >> ft->Gloss) << ft->Gshift) | \ + ((b >> ft->Bloss) << ft->Bshift) | \ + ((a >> ft->Aloss) << ft->Ashift); \ + break; \ + case 4: \ + *((Uint32 *) (buf)) = \ + ((r >> ft->Rloss) << ft->Rshift) | \ + ((g >> ft->Gloss) << ft->Gshift) | \ + ((b >> ft->Bloss) << ft->Bshift) | \ + ((a >> ft->Aloss) << ft->Ashift); \ + break; \ + } + +/* Pretty good idea from Tom Duff :-). */ +#define LOOP_UNROLLED4(code, n, width) \ + n = (width + 3) / 4; \ + switch (width & 3) \ + { \ + case 0: do { code; \ + case 3: code; \ + case 2: code; \ + case 1: code; \ + } while (--n > 0); \ + } + +/* Used in the srcbpp == dstbpp == 1 blend functions */ +#define REPEAT_3(code) \ + code; \ + code; \ + code; + +#define REPEAT_4(code) \ + code; \ + code; \ + code; \ + code; + + +#define BLEND_ADD(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \ + tmp = dR + sR; dR = (tmp <= 255 ? tmp : 255); \ + tmp = dG + sG; dG = (tmp <= 255 ? tmp : 255); \ + tmp = dB + sB; dB = (tmp <= 255 ? tmp : 255); + +#define BLEND_SUB(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \ + tmp = dR - sR; dR = (tmp >= 0 ? tmp : 0); \ + tmp = dG - sG; dG = (tmp >= 0 ? tmp : 0); \ + tmp = dB - sB; dB = (tmp >= 0 ? tmp : 0); + +#define BLEND_MULT(sR, sG, sB, sA, dR, dG, dB, dA) \ + dR = (dR && sR) ? (dR * sR) >> 8 : 0; \ + dG = (dG && sG) ? (dG * sG) >> 8 : 0; \ + dB = (dB && sB) ? (dB * sB) >> 8 : 0; + +#define BLEND_MIN(sR, sG, sB, sA, dR, dG, dB, dA) \ + if(sR < dR) { dR = sR; } \ + if(sG < dG) { dG = sG; } \ + if(sB < dB) { dB = sB; } + +#define BLEND_MAX(sR, sG, sB, sA, dR, dG, dB, dA) \ + if(sR > dR) { dR = sR; } \ + if(sG > dG) { dG = sG; } \ + if(sB > dB) { dB = sB; } + + + + + + +#define BLEND_RGBA_ADD(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \ + tmp = dR + sR; dR = (tmp <= 255 ? tmp : 255); \ + tmp = dG + sG; dG = (tmp <= 255 ? tmp : 255); \ + tmp = dB + sB; dB = (tmp <= 255 ? tmp : 255); \ + tmp = dA + sA; dA = (tmp <= 255 ? tmp : 255); + +#define BLEND_RGBA_SUB(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \ + tmp = dR - sR; dR = (tmp >= 0 ? tmp : 0); \ + tmp = dG - sG; dG = (tmp >= 0 ? tmp : 0); \ + tmp = dB - sB; dB = (tmp >= 0 ? tmp : 0); \ + tmp = dA - sA; dA = (tmp >= 0 ? tmp : 0); + +#define BLEND_RGBA_MULT(sR, sG, sB, sA, dR, dG, dB, dA) \ + dR = (dR && sR) ? (dR * sR) >> 8 : 0; \ + dG = (dG && sG) ? (dG * sG) >> 8 : 0; \ + dB = (dB && sB) ? (dB * sB) >> 8 : 0; \ + dA = (dA && sA) ? (dA * sA) >> 8 : 0; + +#define BLEND_RGBA_MIN(sR, sG, sB, sA, dR, dG, dB, dA) \ + if(sR < dR) { dR = sR; } \ + if(sG < dG) { dG = sG; } \ + if(sB < dB) { dB = sB; } \ + if(sA < dA) { dA = sA; } + +#define BLEND_RGBA_MAX(sR, sG, sB, sA, dR, dG, dB, dA) \ + if(sR > dR) { dR = sR; } \ + if(sG > dG) { dG = sG; } \ + if(sB > dB) { dB = sB; } \ + if(sA > dA) { dA = sA; } + + + + + + + + + + + +#if 1 +/* Choose an alpha blend equation. If the sign is preserved on a right shift + * then use a specialized, faster, equation. Otherwise a more general form, + * where all additions are done before the shift, is needed. +*/ +#if (-1 >> 1) < 0 +#define ALPHA_BLEND_COMP(sC, dC, sA) ((((sC - dC) * sA + sC) >> 8) + dC) +#else +#define ALPHA_BLEND_COMP(sC, dC, sA) (((dC << 8) + (sC - dC) * sA + sC) >> 8) +#endif + +#define ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB, dA) \ + do { \ + if (dA) \ + { \ + dR = ALPHA_BLEND_COMP(sR, dR, sA); \ + dG = ALPHA_BLEND_COMP(sG, dG, sA); \ + dB = ALPHA_BLEND_COMP(sB, dB, sA); \ + dA = sA + dA - ((sA * dA) / 255); \ + } \ + else \ + { \ + dR = sR; \ + dG = sG; \ + dB = sB; \ + dA = sA; \ + } \ + } while(0) + +#define ALPHA_BLEND_PREMULTIPLIED_COMP(sC, dC, sA) (sC + dC - ((dC * sA) >> 8)) + +#define ALPHA_BLEND_PREMULTIPLIED(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \ + do { \ + tmp = ALPHA_BLEND_PREMULTIPLIED_COMP(sR, dR, sA); dR = (tmp > 255 ? 255 : tmp); \ + tmp = ALPHA_BLEND_PREMULTIPLIED_COMP(sG, dG, sA); dG = (tmp > 255 ? 255 : tmp); \ + tmp = ALPHA_BLEND_PREMULTIPLIED_COMP(sB, dB, sA); dB = (tmp > 255 ? 255 : tmp); \ + dA = sA + dA - ((sA * dA) / 255); \ + } while(0) +#elif 0 + +#define ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB, dA) \ + do { \ + if(sA){ \ + if(dA && sA < 255){ \ + int dContrib = dA*(255 - sA)/255; \ + dA = sA+dA - ((sA*dA)/255); \ + dR = (dR*dContrib + sR*sA)/dA; \ + dG = (dG*dContrib + sG*sA)/dA; \ + dB = (dB*dContrib + sB*sA)/dA; \ + }else{ \ + dR = sR; \ + dG = sG; \ + dB = sB; \ + dA = sA; \ + } \ + } \ + } while(0) +#endif + +int +surface_fill_blend (SDL_Surface *surface, SDL_Rect *rect, Uint32 color, + int blendargs); + +void +surface_respect_clip_rect (SDL_Surface *surface, SDL_Rect *rect); + +int +pygame_AlphaBlit (SDL_Surface * src, SDL_Rect * srcrect, + SDL_Surface * dst, SDL_Rect * dstrect, int the_args); + +int +pygame_Blit (SDL_Surface * src, SDL_Rect * srcrect, + SDL_Surface * dst, SDL_Rect * dstrect, int the_args); + +#endif /* SURFACE_H */ diff --git a/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/COPYING b/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/COPYING new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/COPYING.LESSER b/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/COPYING.LESSER new file mode 100644 index 0000000..2d2d780 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/COPYING.LESSER @@ -0,0 +1,510 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/INSTALLER b/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/METADATA b/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/METADATA new file mode 100644 index 0000000..47dad94 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/METADATA @@ -0,0 +1,118 @@ +Metadata-Version: 2.1 +Name: astroid +Version: 2.4.2 +Summary: An abstract syntax tree for Python with inference support. +Home-page: https://github.com/PyCQA/astroid +Author: Python Code Quality Authority +Author-email: code-quality@python.org +License: LGPL +Platform: UNKNOWN +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Software Development :: Quality Assurance +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=3.5 +Requires-Dist: lazy-object-proxy (==1.4.*) +Requires-Dist: six (~=1.12) +Requires-Dist: wrapt (~=1.11) +Requires-Dist: typed-ast (<1.5,>=1.4.0) ; implementation_name == "cpython" and python_version < "3.8" + +Astroid +======= + +.. image:: https://travis-ci.org/PyCQA/astroid.svg?branch=master + :target: https://travis-ci.org/PyCQA/astroid + +.. image:: https://ci.appveyor.com/api/projects/status/co3u42kunguhbh6l/branch/master?svg=true + :alt: AppVeyor Build Status + :target: https://ci.appveyor.com/project/PCManticore/astroid + +.. image:: https://coveralls.io/repos/github/PyCQA/astroid/badge.svg?branch=master + :target: https://coveralls.io/github/PyCQA/astroid?branch=master + +.. image:: https://readthedocs.org/projects/astroid/badge/?version=latest + :target: http://astroid.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/ambv/black + +.. |tideliftlogo| image:: doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png + :width: 75 + :height: 60 + :alt: Tidelift + +.. list-table:: + :widths: 10 100 + + * - |tideliftlogo| + - Professional support for astroid is available as part of the `Tidelift + Subscription`_. Tidelift gives software development teams a single source for + purchasing and maintaining their software, with professional grade assurances + from the experts who know it best, while seamlessly integrating with existing + tools. + +.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-astroid?utm_source=pypi-astroid&utm_medium=referral&utm_campaign=readme + + + +What's this? +------------ + +The aim of this module is to provide a common base representation of +python source code. It is currently the library powering pylint's capabilities. + +It provides a compatible representation which comes from the `_ast` +module. It rebuilds the tree generated by the builtin _ast module by +recursively walking down the AST and building an extended ast. The new +node classes have additional methods and attributes for different +usages. They include some support for static inference and local name +scopes. Furthermore, astroid can also build partial trees by inspecting living +objects. + + +Installation +------------ + +Extract the tarball, jump into the created directory and run:: + + pip install . + + +If you want to do an editable installation, you can run:: + + pip install -e . + + +If you have any questions, please mail the code-quality@python.org +mailing list for support. See +http://mail.python.org/mailman/listinfo/code-quality for subscription +information and archives. + +Documentation +------------- +http://astroid.readthedocs.io/en/latest/ + + +Python Versions +--------------- + +astroid 2.0 is currently available for Python 3 only. If you want Python 2 +support, older versions of astroid will still supported until 2020. + +Test +---- + +Tests are in the 'test' subdirectory. To launch the whole tests suite, you can use +either `tox` or `pytest`:: + + tox + pytest astroid + + diff --git a/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/RECORD b/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/RECORD new file mode 100644 index 0000000..966ae6e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/RECORD @@ -0,0 +1,151 @@ +astroid-2.4.2.dist-info/COPYING,sha256=qxX9UmvY3Rip5368E5ZWv00z6X_HI4zRG_YOK5uGZsY,17987 +astroid-2.4.2.dist-info/COPYING.LESSER,sha256=qb3eVhbs3R6YC0TzYGAO6Hg7H5m4zIOivrFjoKOQ6GE,26527 +astroid-2.4.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +astroid-2.4.2.dist-info/METADATA,sha256=mWlOM8vl6thD_vA7Lzn2yKmQuaX8NukwS72gEpsGM0U,3915 +astroid-2.4.2.dist-info/RECORD,, +astroid-2.4.2.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92 +astroid-2.4.2.dist-info/top_level.txt,sha256=HsdW4O2x7ZXRj6k-agi3RaQybGLobI3VSE-jt4vQUXM,8 +astroid/__init__.py,sha256=zZF5EWBTfOtOcFd62WMbhLAtS2b_Fm-3JJW2AVUUMUI,5655 +astroid/__pkginfo__.py,sha256=G6QlkatrWucF445cQW7Kb2ZSgBzf7TiKLPwqkP0CoQU,2329 +astroid/__pycache__/__init__.cpython-37.pyc,, +astroid/__pycache__/__pkginfo__.cpython-37.pyc,, +astroid/__pycache__/_ast.cpython-37.pyc,, +astroid/__pycache__/arguments.cpython-37.pyc,, +astroid/__pycache__/as_string.cpython-37.pyc,, +astroid/__pycache__/bases.cpython-37.pyc,, +astroid/__pycache__/builder.cpython-37.pyc,, +astroid/__pycache__/context.cpython-37.pyc,, +astroid/__pycache__/decorators.cpython-37.pyc,, +astroid/__pycache__/exceptions.cpython-37.pyc,, +astroid/__pycache__/helpers.cpython-37.pyc,, +astroid/__pycache__/inference.cpython-37.pyc,, +astroid/__pycache__/manager.cpython-37.pyc,, +astroid/__pycache__/mixins.cpython-37.pyc,, +astroid/__pycache__/modutils.cpython-37.pyc,, +astroid/__pycache__/node_classes.cpython-37.pyc,, +astroid/__pycache__/nodes.cpython-37.pyc,, +astroid/__pycache__/objects.cpython-37.pyc,, +astroid/__pycache__/protocols.cpython-37.pyc,, +astroid/__pycache__/raw_building.cpython-37.pyc,, +astroid/__pycache__/rebuilder.cpython-37.pyc,, +astroid/__pycache__/scoped_nodes.cpython-37.pyc,, +astroid/__pycache__/test_utils.cpython-37.pyc,, +astroid/__pycache__/transforms.cpython-37.pyc,, +astroid/__pycache__/util.cpython-37.pyc,, +astroid/_ast.py,sha256=n1U2HblBNrMhsmrjF84HUfIgEZ3PyHyiJJOrOkgiUFk,3385 +astroid/arguments.py,sha256=KJcZn7HeEhj2fZICr-9Pi7iueCNDZRQWh0T7O-qb-AE,12558 +astroid/as_string.py,sha256=8bOZWsGG79TLmlzRzPtmHdanIAqQUFTKiYH873iMhOw,22821 +astroid/bases.py,sha256=wL9C23mHFaj7vhMqM83DdsU6kfP488aEjoFWaO8p6Cg,19175 +astroid/brain/__pycache__/brain_argparse.cpython-37.pyc,, +astroid/brain/__pycache__/brain_attrs.cpython-37.pyc,, +astroid/brain/__pycache__/brain_boto3.cpython-37.pyc,, +astroid/brain/__pycache__/brain_builtin_inference.cpython-37.pyc,, +astroid/brain/__pycache__/brain_collections.cpython-37.pyc,, +astroid/brain/__pycache__/brain_crypt.cpython-37.pyc,, +astroid/brain/__pycache__/brain_curses.cpython-37.pyc,, +astroid/brain/__pycache__/brain_dataclasses.cpython-37.pyc,, +astroid/brain/__pycache__/brain_dateutil.cpython-37.pyc,, +astroid/brain/__pycache__/brain_fstrings.cpython-37.pyc,, +astroid/brain/__pycache__/brain_functools.cpython-37.pyc,, +astroid/brain/__pycache__/brain_gi.cpython-37.pyc,, +astroid/brain/__pycache__/brain_hashlib.cpython-37.pyc,, +astroid/brain/__pycache__/brain_http.cpython-37.pyc,, +astroid/brain/__pycache__/brain_io.cpython-37.pyc,, +astroid/brain/__pycache__/brain_mechanize.cpython-37.pyc,, +astroid/brain/__pycache__/brain_multiprocessing.cpython-37.pyc,, +astroid/brain/__pycache__/brain_namedtuple_enum.cpython-37.pyc,, +astroid/brain/__pycache__/brain_nose.cpython-37.pyc,, +astroid/brain/__pycache__/brain_numpy_core_fromnumeric.cpython-37.pyc,, +astroid/brain/__pycache__/brain_numpy_core_function_base.cpython-37.pyc,, +astroid/brain/__pycache__/brain_numpy_core_multiarray.cpython-37.pyc,, +astroid/brain/__pycache__/brain_numpy_core_numeric.cpython-37.pyc,, +astroid/brain/__pycache__/brain_numpy_core_numerictypes.cpython-37.pyc,, +astroid/brain/__pycache__/brain_numpy_core_umath.cpython-37.pyc,, +astroid/brain/__pycache__/brain_numpy_ndarray.cpython-37.pyc,, +astroid/brain/__pycache__/brain_numpy_random_mtrand.cpython-37.pyc,, +astroid/brain/__pycache__/brain_numpy_utils.cpython-37.pyc,, +astroid/brain/__pycache__/brain_pkg_resources.cpython-37.pyc,, +astroid/brain/__pycache__/brain_pytest.cpython-37.pyc,, +astroid/brain/__pycache__/brain_qt.cpython-37.pyc,, +astroid/brain/__pycache__/brain_random.cpython-37.pyc,, +astroid/brain/__pycache__/brain_re.cpython-37.pyc,, +astroid/brain/__pycache__/brain_responses.cpython-37.pyc,, +astroid/brain/__pycache__/brain_scipy_signal.cpython-37.pyc,, +astroid/brain/__pycache__/brain_six.cpython-37.pyc,, +astroid/brain/__pycache__/brain_ssl.cpython-37.pyc,, +astroid/brain/__pycache__/brain_subprocess.cpython-37.pyc,, +astroid/brain/__pycache__/brain_threading.cpython-37.pyc,, +astroid/brain/__pycache__/brain_typing.cpython-37.pyc,, +astroid/brain/__pycache__/brain_uuid.cpython-37.pyc,, +astroid/brain/brain_argparse.py,sha256=5XqcThekktCIWRlWAMs-R47wkbsOUSnQlsEbLEnRpsY,1041 +astroid/brain/brain_attrs.py,sha256=k8zJqIXsIbQrncthrzyB5NtdPTktgVi9wG7nyl8xMzs,2208 +astroid/brain/brain_boto3.py,sha256=nE8Cw_S_Gp___IszRDRkhEGS6WGrKcF_gTOs3GVxH9k,862 +astroid/brain/brain_builtin_inference.py,sha256=F6_09yM2mmu_u3ad_f9Uumc-q0lDA2CY8v9-PYtUMmA,28793 +astroid/brain/brain_collections.py,sha256=Uqi4VmpLDl0ndQa9x-5tIXRtVdtI6TlwR9KNHmOqLyw,2724 +astroid/brain/brain_crypt.py,sha256=gA7Q4GVuAM4viuTGWM6SNTPQXv5Gr_mFapyKMTRcsJ0,875 +astroid/brain/brain_curses.py,sha256=tDnlCP1bEvleqCMz856yua9mM5um1p_JendFhT4rBFk,3303 +astroid/brain/brain_dataclasses.py,sha256=5WndOYSY0oi2v-Od6KdPte-FKt00LoNRH2riSB4S1os,1647 +astroid/brain/brain_dateutil.py,sha256=GwDrgbaUkKbp3ImLAvuUtPuDBeK3lPh0bNI_Wphm2_8,801 +astroid/brain/brain_fstrings.py,sha256=Jaf-G-wkLecwG4jfjjfR8MDKzgAjjn2mgrrWZQLOAd8,2126 +astroid/brain/brain_functools.py,sha256=Nljy7o2vu16S2amFot4wdTI0U76Yafq-ujVPHOdGgCE,5481 +astroid/brain/brain_gi.py,sha256=oraXhBWyCCxmPEAEvRboeTIho0--ORObvckni00_1wY,7554 +astroid/brain/brain_hashlib.py,sha256=W2cS6-rixdBcre6lPWIuSOqPLCcVji5JBlImdPbV6Qo,2292 +astroid/brain/brain_http.py,sha256=a_8eIACvZetnR2xI4ZARVMuB1WSjyyqM3rCl2DVltMo,10524 +astroid/brain/brain_io.py,sha256=wEY3vvTeV23tYxFn8HHQeyjhb7-jqzwgwNI-kl2Yu-c,1476 +astroid/brain/brain_mechanize.py,sha256=boZxoCxPGSpHC_RccO5U026hyXyhunXR55M9WM1lYTo,895 +astroid/brain/brain_multiprocessing.py,sha256=9OswtRuVBj-KemJ0yp4prz8mrkJDzOzN6n13u2MgmwM,3096 +astroid/brain/brain_namedtuple_enum.py,sha256=eZ8IaHPLLHBakywJ3q4Ogtd2Tk0gtNcAgYAloMAKTjM,15966 +astroid/brain/brain_nose.py,sha256=PSPmme611h7fC8MKuVXbiGw0HZhdmIuoxM6yieZ1OOc,2217 +astroid/brain/brain_numpy_core_fromnumeric.py,sha256=NxiHbcMyQQ3Gpx4KEA5eAmGrpjlPN5rfXjRLjHPOVkw,621 +astroid/brain/brain_numpy_core_function_base.py,sha256=7i6Kge_PviqoWXhbeRgB581xwLo0dxvO54_5UB3ppig,1168 +astroid/brain/brain_numpy_core_multiarray.py,sha256=vr-nBt3EF_z-UmHmcAlJhGorgDXPpQaYX8g4TWu_2Vw,4015 +astroid/brain/brain_numpy_core_numeric.py,sha256=M0RXOym2YC-DaZfRqGNIpr6s-Oh1KHn1okGr3PtPl0k,1384 +astroid/brain/brain_numpy_core_numerictypes.py,sha256=ltlZyQprEbVdUJpC_ERGyXyJsYwzFHx7zGYDLheIonA,8013 +astroid/brain/brain_numpy_core_umath.py,sha256=je6K3ILMen2mgkZseHCOjOgnFV6SaNgAihEi6irhbRk,4436 +astroid/brain/brain_numpy_ndarray.py,sha256=WZbRG7GgOWZ68sEr4JjZUnJHLfUc91xp2mucHeLMrnw,8463 +astroid/brain/brain_numpy_random_mtrand.py,sha256=WRy_CStllgZN2B3Dw2qxXbY4SBrCzu-cdaK230VQHaE,3278 +astroid/brain/brain_numpy_utils.py,sha256=ZYtCVmn1x6P7iwYFohdW3CCrvVT5BCNYe-7jKcz_25s,1875 +astroid/brain/brain_pkg_resources.py,sha256=S_5UED1Zg8ObEJumRdpYGnjxZzemh_G_NFj3p5NGPfc,2262 +astroid/brain/brain_pytest.py,sha256=RYp7StKnC22n4vFJkHH5h7DAFWtouIcg-ZCXQ8Oz3xk,2390 +astroid/brain/brain_qt.py,sha256=m2s4YXFrnOynWDf9omHOBLVqFriBdGJAmvarJCxiffM,2536 +astroid/brain/brain_random.py,sha256=2RZY-QEXMNWp7E6h0l0-ke-DtjKTOFlTdjiQZi3XdQc,2432 +astroid/brain/brain_re.py,sha256=le7VJHUAf80HyE_aQCh7_8FyDVK6JwNWA--c9RaMVQ8,1128 +astroid/brain/brain_responses.py,sha256=GIAGkwMEGEZ9bJHI9uBGs_UmUy3DRBtjI_j_8dLBVsc,1534 +astroid/brain/brain_scipy_signal.py,sha256=IKNnGXzEH5FKhPHM2hv57pNdo1emThWwZ0ksckdsOeE,2255 +astroid/brain/brain_six.py,sha256=79aws4au6ZQJuf-YhBT4R-9wuVOwg-u0T42aUhCswK0,6187 +astroid/brain/brain_ssl.py,sha256=9dMCTQ0JsX2gpdmBkYO7BByw3zN23oXRL8Svo2S1V28,3722 +astroid/brain/brain_subprocess.py,sha256=yp-HsE69uh7T7C1kZYciAdRMQ48aNT51lI032oDppDk,4688 +astroid/brain/brain_threading.py,sha256=dY8YwZ-zrB30_CQFYWh4AK3qUVssakgwLVwunQ6yYkU,837 +astroid/brain/brain_typing.py,sha256=iFw33beNCitCJjJNvccIY6SsFJcdKVDdl-56DxDioh0,2780 +astroid/brain/brain_uuid.py,sha256=YzfiOXavu515cS1prsKGfY4-12g_KWT0Yr4-5ti0v74,569 +astroid/builder.py,sha256=72JlHrXRF9sS5zh1mqXlRHGFg88Efc2rm9yRJKDIShA,16891 +astroid/context.py,sha256=MpwiEzWZ39a4WyqYgbSmePL2nZLqALt1gjrvi3TP35U,5169 +astroid/decorators.py,sha256=z_wTjsiMlu_ElHtYUUuxaB2F_s4G0Ks7bmtCZS3IQ_Q,4283 +astroid/exceptions.py,sha256=GLiZo9BdWILJShO-il8ra-tPZqaODMAX987F--LWv2w,6891 +astroid/helpers.py,sha256=3YoJeVLoS-66T_abDorUNHiP8m2R7-faNnpc5yPJhrc,9448 +astroid/inference.py,sha256=UiKKPRYqb5JINQIw1lSdBniHhXjCohCGAqycp2GkVI4,34686 +astroid/interpreter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/interpreter/__pycache__/__init__.cpython-37.pyc,, +astroid/interpreter/__pycache__/dunder_lookup.cpython-37.pyc,, +astroid/interpreter/__pycache__/objectmodel.cpython-37.pyc,, +astroid/interpreter/_import/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/interpreter/_import/__pycache__/__init__.cpython-37.pyc,, +astroid/interpreter/_import/__pycache__/spec.cpython-37.pyc,, +astroid/interpreter/_import/__pycache__/util.cpython-37.pyc,, +astroid/interpreter/_import/spec.py,sha256=rZ9kX3I0xPo_pFAYWeOk2Xbi4ZIg_5bRsouZgxOXegA,11436 +astroid/interpreter/_import/util.py,sha256=inubUz6F3_kaMFaeleKUW6E6wCMIPrhU882zvwEZ02I,255 +astroid/interpreter/dunder_lookup.py,sha256=dP-AZU_aGPNt03b1ttrMglxzeU3NtgnG0MfpSLPH6sg,2155 +astroid/interpreter/objectmodel.py,sha256=bbPIaamrqrx7WHtG5YNs9dbrlDC00GrJPxPGoSTsnqg,25792 +astroid/manager.py,sha256=jmEm9uH00mPA2Y68s10xrPNbZaadv_2c-CWluB3SYoE,13701 +astroid/mixins.py,sha256=F2rv2Ow7AU3YT_2jitVJik95ZWRVK6hpf8BrkkspzUY,5571 +astroid/modutils.py,sha256=GBW5Z691eqf6VAnPsZzeQ0WYzrl-0GGTHkkZNb_9XRQ,23242 +astroid/node_classes.py,sha256=cBRkZ_u8ZoRkiXznYWq1L3I7cO-P9nGowNCy8T7Qpzk,142740 +astroid/nodes.py,sha256=WoyRe22GNVRc2TRHWOUlqdxCdOVD8GKOq9v1LpPhkr8,2978 +astroid/objects.py,sha256=caKeQPBtrrfqa1q844vkehXwpdMzvph5YJdJJdbD_m8,11035 +astroid/protocols.py,sha256=m1ZHvKvFQZSZp993na4f9s8V17kqNPRfH-XpQc8gJ7c,27396 +astroid/raw_building.py,sha256=PI70y2mPQ7JURDVyNZ6deeBFkvjNNaOQBkuo0VPbvQ4,17340 +astroid/rebuilder.py,sha256=xn82eWlxzGcDd2VACUnNNWCxArfV9HI8g67fUip0XFk,39411 +astroid/scoped_nodes.py,sha256=fUrmTyltaLCr9-i8RdC2e5dPA9O8w946Mvvft8mgXdM,98203 +astroid/test_utils.py,sha256=axGB3j6ZEaVspL1ZgPizKgWehNEREb58Z97U7mQ-GA8,2319 +astroid/transforms.py,sha256=1npwJWcQUSIjcpcWd1pc-dJhtHOyiboQHsETAIQd5co,3377 +astroid/util.py,sha256=jg5LnqbWSZTZP1KgpxGBuC6Lfwhn9Jb2T2TohXghmC0,4785 diff --git a/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/WHEEL b/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/WHEEL new file mode 100644 index 0000000..b552003 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.34.2) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/top_level.txt b/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/top_level.txt new file mode 100644 index 0000000..450d4fe --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid-2.4.2.dist-info/top_level.txt @@ -0,0 +1 @@ +astroid diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/__init__.py b/Display/.venv/lib/python3.7/site-packages/astroid/__init__.py new file mode 100644 index 0000000..e869274 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/__init__.py @@ -0,0 +1,168 @@ +# Copyright (c) 2006-2013, 2015 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2014 Google, Inc. +# Copyright (c) 2014 Eevee (Alex Munroe) +# Copyright (c) 2015-2016, 2018 Claudiu Popa +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2016 Derek Gustafson +# Copyright (c) 2016 Moises Lopez +# Copyright (c) 2018 Bryce Guinta +# Copyright (c) 2019 Nick Drozd + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""Python Abstract Syntax Tree New Generation + +The aim of this module is to provide a common base representation of +python source code for projects such as pychecker, pyreverse, +pylint... Well, actually the development of this library is essentially +governed by pylint's needs. + +It extends class defined in the python's _ast module with some +additional methods and attributes. Instance attributes are added by a +builder object, which can either generate extended ast (let's call +them astroid ;) by visiting an existent ast tree or by inspecting living +object. Methods are added by monkey patching ast classes. + +Main modules are: + +* nodes and scoped_nodes for more information about methods and + attributes added to different node classes + +* the manager contains a high level object to get astroid trees from + source files and living objects. It maintains a cache of previously + constructed tree for quick access + +* builder contains the class responsible to build astroid trees +""" + +import enum +import itertools +import os +import sys + +import wrapt + + +_Context = enum.Enum("Context", "Load Store Del") +Load = _Context.Load +Store = _Context.Store +Del = _Context.Del +del _Context + + +# pylint: disable=wrong-import-order,wrong-import-position +from .__pkginfo__ import version as __version__ + +# WARNING: internal imports order matters ! + +# pylint: disable=redefined-builtin + +# make all exception classes accessible from astroid package +from astroid.exceptions import * + +# make all node classes accessible from astroid package +from astroid.nodes import * + +# trigger extra monkey-patching +from astroid import inference + +# more stuff available +from astroid import raw_building +from astroid.bases import BaseInstance, Instance, BoundMethod, UnboundMethod +from astroid.node_classes import are_exclusive, unpack_infer +from astroid.scoped_nodes import builtin_lookup +from astroid.builder import parse, extract_node +from astroid.util import Uninferable + +# make a manager instance (borg) accessible from astroid package +from astroid.manager import AstroidManager + +MANAGER = AstroidManager() +del AstroidManager + +# transform utilities (filters and decorator) + + +# pylint: disable=dangerous-default-value +@wrapt.decorator +def _inference_tip_cached(func, instance, args, kwargs, _cache={}): + """Cache decorator used for inference tips""" + node = args[0] + try: + return iter(_cache[func, node]) + except KeyError: + result = func(*args, **kwargs) + # Need to keep an iterator around + original, copy = itertools.tee(result) + _cache[func, node] = list(copy) + return original + + +# pylint: enable=dangerous-default-value + + +def inference_tip(infer_function, raise_on_overwrite=False): + """Given an instance specific inference function, return a function to be + given to MANAGER.register_transform to set this inference function. + + :param bool raise_on_overwrite: Raise an `InferenceOverwriteError` + if the inference tip will overwrite another. Used for debugging + + Typical usage + + .. sourcecode:: python + + MANAGER.register_transform(Call, inference_tip(infer_named_tuple), + predicate) + + .. Note:: + + Using an inference tip will override + any previously set inference tip for the given + node. Use a predicate in the transform to prevent + excess overwrites. + """ + + def transform(node, infer_function=infer_function): + if ( + raise_on_overwrite + and node._explicit_inference is not None + and node._explicit_inference is not infer_function + ): + raise InferenceOverwriteError( + "Inference already set to {existing_inference}. " + "Trying to overwrite with {new_inference} for {node}".format( + existing_inference=infer_function, + new_inference=node._explicit_inference, + node=node, + ) + ) + # pylint: disable=no-value-for-parameter + node._explicit_inference = _inference_tip_cached(infer_function) + return node + + return transform + + +def register_module_extender(manager, module_name, get_extension_mod): + def transform(node): + extension_module = get_extension_mod() + for name, objs in extension_module.locals.items(): + node.locals[name] = objs + for obj in objs: + if obj.parent is extension_module: + obj.parent = node + + manager.register_transform(Module, transform, lambda n: n.name == module_name) + + +# load brain plugins +BRAIN_MODULES_DIR = os.path.join(os.path.dirname(__file__), "brain") +if BRAIN_MODULES_DIR not in sys.path: + # add it to the end of the list so user path take precedence + sys.path.append(BRAIN_MODULES_DIR) +# load modules in this directory +for module in os.listdir(BRAIN_MODULES_DIR): + if module.endswith(".py"): + __import__(module[:-3]) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/__pkginfo__.py b/Display/.venv/lib/python3.7/site-packages/astroid/__pkginfo__.py new file mode 100644 index 0000000..fd8e132 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/__pkginfo__.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2014-2019 Claudiu Popa +# Copyright (c) 2014 Google, Inc. +# Copyright (c) 2015-2017 Ceridwen +# Copyright (c) 2015 Florian Bruhin +# Copyright (c) 2015 Radosław Ganczarek +# Copyright (c) 2016 Moises Lopez +# Copyright (c) 2017 Hugo +# Copyright (c) 2017 Łukasz Rogalski +# Copyright (c) 2017 Calen Pennington +# Copyright (c) 2018 Ville Skyttä +# Copyright (c) 2018 Ashley Whetter +# Copyright (c) 2018 Bryce Guinta +# Copyright (c) 2019 Uilian Ries +# Copyright (c) 2019 Thomas Hisch +# Copyright (c) 2020 Michael + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""astroid packaging information""" + +version = "2.4.2" +numversion = tuple(int(elem) for elem in version.split(".") if elem.isdigit()) + +extras_require = {} +install_requires = [ + "lazy_object_proxy==1.4.*", + "six~=1.12", + "wrapt~=1.11", + 'typed-ast>=1.4.0,<1.5;implementation_name== "cpython" and python_version<"3.8"', +] + +# pylint: disable=redefined-builtin; why license is a builtin anyway? +license = "LGPL" + +author = "Python Code Quality Authority" +author_email = "code-quality@python.org" +mailinglist = "mailto://%s" % author_email +web = "https://github.com/PyCQA/astroid" + +description = "An abstract syntax tree for Python with inference support." + +classifiers = [ + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Software Development :: Quality Assurance", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", +] diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/_ast.py b/Display/.venv/lib/python3.7/site-packages/astroid/_ast.py new file mode 100644 index 0000000..34b74c5 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/_ast.py @@ -0,0 +1,131 @@ +import ast +from collections import namedtuple +from functools import partial +from typing import Optional +import sys + +import astroid + +_ast_py3 = None +try: + import typed_ast.ast3 as _ast_py3 +except ImportError: + pass + + +PY38 = sys.version_info[:2] >= (3, 8) +if PY38: + # On Python 3.8, typed_ast was merged back into `ast` + _ast_py3 = ast + + +FunctionType = namedtuple("FunctionType", ["argtypes", "returns"]) + + +class ParserModule( + namedtuple( + "ParserModule", + [ + "module", + "unary_op_classes", + "cmp_op_classes", + "bool_op_classes", + "bin_op_classes", + "context_classes", + ], + ) +): + def parse(self, string: str, type_comments=True): + if self.module is _ast_py3: + if PY38: + parse_func = partial(self.module.parse, type_comments=type_comments) + else: + parse_func = partial( + self.module.parse, feature_version=sys.version_info.minor + ) + else: + parse_func = self.module.parse + return parse_func(string) + + +def parse_function_type_comment(type_comment: str) -> Optional[FunctionType]: + """Given a correct type comment, obtain a FunctionType object""" + if _ast_py3 is None: + return None + + func_type = _ast_py3.parse(type_comment, "", "func_type") + return FunctionType(argtypes=func_type.argtypes, returns=func_type.returns) + + +def get_parser_module(type_comments=True) -> ParserModule: + if not type_comments: + parser_module = ast + else: + parser_module = _ast_py3 + parser_module = parser_module or ast + + unary_op_classes = _unary_operators_from_module(parser_module) + cmp_op_classes = _compare_operators_from_module(parser_module) + bool_op_classes = _bool_operators_from_module(parser_module) + bin_op_classes = _binary_operators_from_module(parser_module) + context_classes = _contexts_from_module(parser_module) + + return ParserModule( + parser_module, + unary_op_classes, + cmp_op_classes, + bool_op_classes, + bin_op_classes, + context_classes, + ) + + +def _unary_operators_from_module(module): + return {module.UAdd: "+", module.USub: "-", module.Not: "not", module.Invert: "~"} + + +def _binary_operators_from_module(module): + binary_operators = { + module.Add: "+", + module.BitAnd: "&", + module.BitOr: "|", + module.BitXor: "^", + module.Div: "/", + module.FloorDiv: "//", + module.MatMult: "@", + module.Mod: "%", + module.Mult: "*", + module.Pow: "**", + module.Sub: "-", + module.LShift: "<<", + module.RShift: ">>", + } + return binary_operators + + +def _bool_operators_from_module(module): + return {module.And: "and", module.Or: "or"} + + +def _compare_operators_from_module(module): + return { + module.Eq: "==", + module.Gt: ">", + module.GtE: ">=", + module.In: "in", + module.Is: "is", + module.IsNot: "is not", + module.Lt: "<", + module.LtE: "<=", + module.NotEq: "!=", + module.NotIn: "not in", + } + + +def _contexts_from_module(module): + return { + module.Load: astroid.Load, + module.Store: astroid.Store, + module.Del: astroid.Del, + module.Param: astroid.Store, + } diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/arguments.py b/Display/.venv/lib/python3.7/site-packages/astroid/arguments.py new file mode 100644 index 0000000..5f4d909 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/arguments.py @@ -0,0 +1,300 @@ +# Copyright (c) 2015-2016, 2018-2020 Claudiu Popa +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2018 Bryce Guinta +# Copyright (c) 2018 Nick Drozd +# Copyright (c) 2018 Anthony Sottile + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + + +from astroid import bases +from astroid import context as contextmod +from astroid import exceptions +from astroid import nodes +from astroid import util + + +class CallSite: + """Class for understanding arguments passed into a call site + + It needs a call context, which contains the arguments and the + keyword arguments that were passed into a given call site. + In order to infer what an argument represents, call :meth:`infer_argument` + with the corresponding function node and the argument name. + + :param callcontext: + An instance of :class:`astroid.context.CallContext`, that holds + the arguments for the call site. + :param argument_context_map: + Additional contexts per node, passed in from :attr:`astroid.context.Context.extra_context` + :param context: + An instance of :class:`astroid.context.Context`. + """ + + def __init__(self, callcontext, argument_context_map=None, context=None): + if argument_context_map is None: + argument_context_map = {} + self.argument_context_map = argument_context_map + args = callcontext.args + keywords = callcontext.keywords + self.duplicated_keywords = set() + self._unpacked_args = self._unpack_args(args, context=context) + self._unpacked_kwargs = self._unpack_keywords(keywords, context=context) + + self.positional_arguments = [ + arg for arg in self._unpacked_args if arg is not util.Uninferable + ] + self.keyword_arguments = { + key: value + for key, value in self._unpacked_kwargs.items() + if value is not util.Uninferable + } + + @classmethod + def from_call(cls, call_node, context=None): + """Get a CallSite object from the given Call node. + + :param context: + An instance of :class:`astroid.context.Context` that will be used + to force a single inference path. + """ + + # Determine the callcontext from the given `context` object if any. + context = context or contextmod.InferenceContext() + callcontext = contextmod.CallContext(call_node.args, call_node.keywords) + return cls(callcontext, context=context) + + def has_invalid_arguments(self): + """Check if in the current CallSite were passed *invalid* arguments + + This can mean multiple things. For instance, if an unpacking + of an invalid object was passed, then this method will return True. + Other cases can be when the arguments can't be inferred by astroid, + for example, by passing objects which aren't known statically. + """ + return len(self.positional_arguments) != len(self._unpacked_args) + + def has_invalid_keywords(self): + """Check if in the current CallSite were passed *invalid* keyword arguments + + For instance, unpacking a dictionary with integer keys is invalid + (**{1:2}), because the keys must be strings, which will make this + method to return True. Other cases where this might return True if + objects which can't be inferred were passed. + """ + return len(self.keyword_arguments) != len(self._unpacked_kwargs) + + def _unpack_keywords(self, keywords, context=None): + values = {} + context = context or contextmod.InferenceContext() + context.extra_context = self.argument_context_map + for name, value in keywords: + if name is None: + # Then it's an unpacking operation (**) + try: + inferred = next(value.infer(context=context)) + except exceptions.InferenceError: + values[name] = util.Uninferable + continue + + if not isinstance(inferred, nodes.Dict): + # Not something we can work with. + values[name] = util.Uninferable + continue + + for dict_key, dict_value in inferred.items: + try: + dict_key = next(dict_key.infer(context=context)) + except exceptions.InferenceError: + values[name] = util.Uninferable + continue + if not isinstance(dict_key, nodes.Const): + values[name] = util.Uninferable + continue + if not isinstance(dict_key.value, str): + values[name] = util.Uninferable + continue + if dict_key.value in values: + # The name is already in the dictionary + values[dict_key.value] = util.Uninferable + self.duplicated_keywords.add(dict_key.value) + continue + values[dict_key.value] = dict_value + else: + values[name] = value + return values + + def _unpack_args(self, args, context=None): + values = [] + context = context or contextmod.InferenceContext() + context.extra_context = self.argument_context_map + for arg in args: + if isinstance(arg, nodes.Starred): + try: + inferred = next(arg.value.infer(context=context)) + except exceptions.InferenceError: + values.append(util.Uninferable) + continue + + if inferred is util.Uninferable: + values.append(util.Uninferable) + continue + if not hasattr(inferred, "elts"): + values.append(util.Uninferable) + continue + values.extend(inferred.elts) + else: + values.append(arg) + return values + + def infer_argument(self, funcnode, name, context): + """infer a function argument value according to the call context + + Arguments: + funcnode: The function being called. + name: The name of the argument whose value is being inferred. + context: Inference context object + """ + if name in self.duplicated_keywords: + raise exceptions.InferenceError( + "The arguments passed to {func!r} " " have duplicate keywords.", + call_site=self, + func=funcnode, + arg=name, + context=context, + ) + + # Look into the keywords first, maybe it's already there. + try: + return self.keyword_arguments[name].infer(context) + except KeyError: + pass + + # Too many arguments given and no variable arguments. + if len(self.positional_arguments) > len(funcnode.args.args): + if not funcnode.args.vararg: + raise exceptions.InferenceError( + "Too many positional arguments " + "passed to {func!r} that does " + "not have *args.", + call_site=self, + func=funcnode, + arg=name, + context=context, + ) + + positional = self.positional_arguments[: len(funcnode.args.args)] + vararg = self.positional_arguments[len(funcnode.args.args) :] + argindex = funcnode.args.find_argname(name)[0] + kwonlyargs = {arg.name for arg in funcnode.args.kwonlyargs} + kwargs = { + key: value + for key, value in self.keyword_arguments.items() + if key not in kwonlyargs + } + # If there are too few positionals compared to + # what the function expects to receive, check to see + # if the missing positional arguments were passed + # as keyword arguments and if so, place them into the + # positional args list. + if len(positional) < len(funcnode.args.args): + for func_arg in funcnode.args.args: + if func_arg.name in kwargs: + arg = kwargs.pop(func_arg.name) + positional.append(arg) + + if argindex is not None: + # 2. first argument of instance/class method + if argindex == 0 and funcnode.type in ("method", "classmethod"): + if context.boundnode is not None: + boundnode = context.boundnode + else: + # XXX can do better ? + boundnode = funcnode.parent.frame() + + if isinstance(boundnode, nodes.ClassDef): + # Verify that we're accessing a method + # of the metaclass through a class, as in + # `cls.metaclass_method`. In this case, the + # first argument is always the class. + method_scope = funcnode.parent.scope() + if method_scope is boundnode.metaclass(): + return iter((boundnode,)) + + if funcnode.type == "method": + if not isinstance(boundnode, bases.Instance): + boundnode = boundnode.instantiate_class() + return iter((boundnode,)) + if funcnode.type == "classmethod": + return iter((boundnode,)) + # if we have a method, extract one position + # from the index, so we'll take in account + # the extra parameter represented by `self` or `cls` + if funcnode.type in ("method", "classmethod"): + argindex -= 1 + # 2. search arg index + try: + return self.positional_arguments[argindex].infer(context) + except IndexError: + pass + + if funcnode.args.kwarg == name: + # It wants all the keywords that were passed into + # the call site. + if self.has_invalid_keywords(): + raise exceptions.InferenceError( + "Inference failed to find values for all keyword arguments " + "to {func!r}: {unpacked_kwargs!r} doesn't correspond to " + "{keyword_arguments!r}.", + keyword_arguments=self.keyword_arguments, + unpacked_kwargs=self._unpacked_kwargs, + call_site=self, + func=funcnode, + arg=name, + context=context, + ) + kwarg = nodes.Dict( + lineno=funcnode.args.lineno, + col_offset=funcnode.args.col_offset, + parent=funcnode.args, + ) + kwarg.postinit( + [(nodes.const_factory(key), value) for key, value in kwargs.items()] + ) + return iter((kwarg,)) + if funcnode.args.vararg == name: + # It wants all the args that were passed into + # the call site. + if self.has_invalid_arguments(): + raise exceptions.InferenceError( + "Inference failed to find values for all positional " + "arguments to {func!r}: {unpacked_args!r} doesn't " + "correspond to {positional_arguments!r}.", + positional_arguments=self.positional_arguments, + unpacked_args=self._unpacked_args, + call_site=self, + func=funcnode, + arg=name, + context=context, + ) + args = nodes.Tuple( + lineno=funcnode.args.lineno, + col_offset=funcnode.args.col_offset, + parent=funcnode.args, + ) + args.postinit(vararg) + return iter((args,)) + + # Check if it's a default parameter. + try: + return funcnode.args.default_value(name).infer(context) + except exceptions.NoDefault: + pass + raise exceptions.InferenceError( + "No value found for argument {name} to " "{func!r}", + call_site=self, + func=funcnode, + arg=name, + context=context, + ) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/as_string.py b/Display/.venv/lib/python3.7/site-packages/astroid/as_string.py new file mode 100644 index 0000000..653411f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/as_string.py @@ -0,0 +1,631 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2010 Daniel Harding +# Copyright (c) 2013-2016, 2018-2020 Claudiu Popa +# Copyright (c) 2013-2014 Google, Inc. +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2016 Jared Garst +# Copyright (c) 2016 Jakub Wilk +# Copyright (c) 2017, 2019 Łukasz Rogalski +# Copyright (c) 2017 rr- +# Copyright (c) 2018 Serhiy Storchaka +# Copyright (c) 2018 Ville Skyttä +# Copyright (c) 2018 brendanator +# Copyright (c) 2018 Nick Drozd +# Copyright (c) 2019 Alex Hall +# Copyright (c) 2019 Hugo van Kemenade + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""This module renders Astroid nodes as string: + +* :func:`to_code` function return equivalent (hopefully valid) python string + +* :func:`dump` function return an internal representation of nodes found + in the tree, useful for debugging or understanding the tree structure +""" + +# pylint: disable=unused-argument + +DOC_NEWLINE = "\0" + + +class AsStringVisitor: + """Visitor to render an Astroid node as a valid python code string""" + + def __init__(self, indent): + self.indent = indent + + def __call__(self, node): + """Makes this visitor behave as a simple function""" + return node.accept(self).replace(DOC_NEWLINE, "\n") + + def _docs_dedent(self, doc): + """Stop newlines in docs being indented by self._stmt_list""" + return '\n%s"""%s"""' % (self.indent, doc.replace("\n", DOC_NEWLINE)) + + def _stmt_list(self, stmts, indent=True): + """return a list of nodes to string""" + stmts = "\n".join(nstr for nstr in [n.accept(self) for n in stmts] if nstr) + if indent: + return self.indent + stmts.replace("\n", "\n" + self.indent) + + return stmts + + def _precedence_parens(self, node, child, is_left=True): + """Wrap child in parens only if required to keep same semantics""" + if self._should_wrap(node, child, is_left): + return "(%s)" % child.accept(self) + + return child.accept(self) + + def _should_wrap(self, node, child, is_left): + """Wrap child if: + - it has lower precedence + - same precedence with position opposite to associativity direction + """ + node_precedence = node.op_precedence() + child_precedence = child.op_precedence() + + if node_precedence > child_precedence: + # 3 * (4 + 5) + return True + + if ( + node_precedence == child_precedence + and is_left != node.op_left_associative() + ): + # 3 - (4 - 5) + # (2**3)**4 + return True + + return False + + ## visit_ methods ########################################### + + def visit_await(self, node): + return "await %s" % node.value.accept(self) + + def visit_asyncwith(self, node): + return "async %s" % self.visit_with(node) + + def visit_asyncfor(self, node): + return "async %s" % self.visit_for(node) + + def visit_arguments(self, node): + """return an astroid.Function node as string""" + return node.format_args() + + def visit_assignattr(self, node): + """return an astroid.AssAttr node as string""" + return self.visit_attribute(node) + + def visit_assert(self, node): + """return an astroid.Assert node as string""" + if node.fail: + return "assert %s, %s" % (node.test.accept(self), node.fail.accept(self)) + return "assert %s" % node.test.accept(self) + + def visit_assignname(self, node): + """return an astroid.AssName node as string""" + return node.name + + def visit_assign(self, node): + """return an astroid.Assign node as string""" + lhs = " = ".join(n.accept(self) for n in node.targets) + return "%s = %s" % (lhs, node.value.accept(self)) + + def visit_augassign(self, node): + """return an astroid.AugAssign node as string""" + return "%s %s %s" % (node.target.accept(self), node.op, node.value.accept(self)) + + def visit_annassign(self, node): + """Return an astroid.AugAssign node as string""" + + target = node.target.accept(self) + annotation = node.annotation.accept(self) + if node.value is None: + return "%s: %s" % (target, annotation) + return "%s: %s = %s" % (target, annotation, node.value.accept(self)) + + def visit_repr(self, node): + """return an astroid.Repr node as string""" + return "`%s`" % node.value.accept(self) + + def visit_binop(self, node): + """return an astroid.BinOp node as string""" + left = self._precedence_parens(node, node.left) + right = self._precedence_parens(node, node.right, is_left=False) + if node.op == "**": + return "%s%s%s" % (left, node.op, right) + + return "%s %s %s" % (left, node.op, right) + + def visit_boolop(self, node): + """return an astroid.BoolOp node as string""" + values = ["%s" % self._precedence_parens(node, n) for n in node.values] + return (" %s " % node.op).join(values) + + def visit_break(self, node): + """return an astroid.Break node as string""" + return "break" + + def visit_call(self, node): + """return an astroid.Call node as string""" + expr_str = self._precedence_parens(node, node.func) + args = [arg.accept(self) for arg in node.args] + if node.keywords: + keywords = [kwarg.accept(self) for kwarg in node.keywords] + else: + keywords = [] + + args.extend(keywords) + return "%s(%s)" % (expr_str, ", ".join(args)) + + def visit_classdef(self, node): + """return an astroid.ClassDef node as string""" + decorate = node.decorators.accept(self) if node.decorators else "" + args = [n.accept(self) for n in node.bases] + if node._metaclass and not node.has_metaclass_hack(): + args.append("metaclass=" + node._metaclass.accept(self)) + args += [n.accept(self) for n in node.keywords] + args = "(%s)" % ", ".join(args) if args else "" + docs = self._docs_dedent(node.doc) if node.doc else "" + return "\n\n%sclass %s%s:%s\n%s\n" % ( + decorate, + node.name, + args, + docs, + self._stmt_list(node.body), + ) + + def visit_compare(self, node): + """return an astroid.Compare node as string""" + rhs_str = " ".join( + [ + "%s %s" % (op, self._precedence_parens(node, expr, is_left=False)) + for op, expr in node.ops + ] + ) + return "%s %s" % (self._precedence_parens(node, node.left), rhs_str) + + def visit_comprehension(self, node): + """return an astroid.Comprehension node as string""" + ifs = "".join(" if %s" % n.accept(self) for n in node.ifs) + generated = "for %s in %s%s" % ( + node.target.accept(self), + node.iter.accept(self), + ifs, + ) + return "%s%s" % ("async " if node.is_async else "", generated) + + def visit_const(self, node): + """return an astroid.Const node as string""" + if node.value is Ellipsis: + return "..." + return repr(node.value) + + def visit_continue(self, node): + """return an astroid.Continue node as string""" + return "continue" + + def visit_delete(self, node): # XXX check if correct + """return an astroid.Delete node as string""" + return "del %s" % ", ".join(child.accept(self) for child in node.targets) + + def visit_delattr(self, node): + """return an astroid.DelAttr node as string""" + return self.visit_attribute(node) + + def visit_delname(self, node): + """return an astroid.DelName node as string""" + return node.name + + def visit_decorators(self, node): + """return an astroid.Decorators node as string""" + return "@%s\n" % "\n@".join(item.accept(self) for item in node.nodes) + + def visit_dict(self, node): + """return an astroid.Dict node as string""" + return "{%s}" % ", ".join(self._visit_dict(node)) + + def _visit_dict(self, node): + for key, value in node.items: + key = key.accept(self) + value = value.accept(self) + if key == "**": + # It can only be a DictUnpack node. + yield key + value + else: + yield "%s: %s" % (key, value) + + def visit_dictunpack(self, node): + return "**" + + def visit_dictcomp(self, node): + """return an astroid.DictComp node as string""" + return "{%s: %s %s}" % ( + node.key.accept(self), + node.value.accept(self), + " ".join(n.accept(self) for n in node.generators), + ) + + def visit_expr(self, node): + """return an astroid.Discard node as string""" + return node.value.accept(self) + + def visit_emptynode(self, node): + """dummy method for visiting an Empty node""" + return "" + + def visit_excepthandler(self, node): + if node.type: + if node.name: + excs = "except %s as %s" % ( + node.type.accept(self), + node.name.accept(self), + ) + else: + excs = "except %s" % node.type.accept(self) + else: + excs = "except" + return "%s:\n%s" % (excs, self._stmt_list(node.body)) + + def visit_ellipsis(self, node): + """return an astroid.Ellipsis node as string""" + return "..." + + def visit_empty(self, node): + """return an Empty node as string""" + return "" + + def visit_exec(self, node): + """return an astroid.Exec node as string""" + if node.locals: + return "exec %s in %s, %s" % ( + node.expr.accept(self), + node.locals.accept(self), + node.globals.accept(self), + ) + if node.globals: + return "exec %s in %s" % (node.expr.accept(self), node.globals.accept(self)) + return "exec %s" % node.expr.accept(self) + + def visit_extslice(self, node): + """return an astroid.ExtSlice node as string""" + return ", ".join(dim.accept(self) for dim in node.dims) + + def visit_for(self, node): + """return an astroid.For node as string""" + fors = "for %s in %s:\n%s" % ( + node.target.accept(self), + node.iter.accept(self), + self._stmt_list(node.body), + ) + if node.orelse: + fors = "%s\nelse:\n%s" % (fors, self._stmt_list(node.orelse)) + return fors + + def visit_importfrom(self, node): + """return an astroid.ImportFrom node as string""" + return "from %s import %s" % ( + "." * (node.level or 0) + node.modname, + _import_string(node.names), + ) + + def visit_joinedstr(self, node): + string = "".join( + # Use repr on the string literal parts + # to get proper escapes, e.g. \n, \\, \" + # But strip the quotes off the ends + # (they will always be one character: ' or ") + repr(value.value)[1:-1] + # Literal braces must be doubled to escape them + .replace("{", "{{").replace("}", "}}") + # Each value in values is either a string literal (Const) + # or a FormattedValue + if type(value).__name__ == "Const" else value.accept(self) + for value in node.values + ) + + # Try to find surrounding quotes that don't appear at all in the string. + # Because the formatted values inside {} can't contain backslash (\) + # using a triple quote is sometimes necessary + for quote in ["'", '"', '"""', "'''"]: + if quote not in string: + break + + return "f" + quote + string + quote + + def visit_formattedvalue(self, node): + result = node.value.accept(self) + if node.conversion and node.conversion >= 0: + # e.g. if node.conversion == 114: result += "!r" + result += "!" + chr(node.conversion) + if node.format_spec: + # The format spec is itself a JoinedString, i.e. an f-string + # We strip the f and quotes of the ends + result += ":" + node.format_spec.accept(self)[2:-1] + return "{%s}" % result + + def handle_functiondef(self, node, keyword): + """return a (possibly async) function definition node as string""" + decorate = node.decorators.accept(self) if node.decorators else "" + docs = self._docs_dedent(node.doc) if node.doc else "" + trailer = ":" + if node.returns: + return_annotation = " -> " + node.returns.as_string() + trailer = return_annotation + ":" + def_format = "\n%s%s %s(%s)%s%s\n%s" + return def_format % ( + decorate, + keyword, + node.name, + node.args.accept(self), + trailer, + docs, + self._stmt_list(node.body), + ) + + def visit_functiondef(self, node): + """return an astroid.FunctionDef node as string""" + return self.handle_functiondef(node, "def") + + def visit_asyncfunctiondef(self, node): + """return an astroid.AsyncFunction node as string""" + return self.handle_functiondef(node, "async def") + + def visit_generatorexp(self, node): + """return an astroid.GeneratorExp node as string""" + return "(%s %s)" % ( + node.elt.accept(self), + " ".join(n.accept(self) for n in node.generators), + ) + + def visit_attribute(self, node): + """return an astroid.Getattr node as string""" + left = self._precedence_parens(node, node.expr) + if left.isdigit(): + left = "(%s)" % left + return "%s.%s" % (left, node.attrname) + + def visit_global(self, node): + """return an astroid.Global node as string""" + return "global %s" % ", ".join(node.names) + + def visit_if(self, node): + """return an astroid.If node as string""" + ifs = ["if %s:\n%s" % (node.test.accept(self), self._stmt_list(node.body))] + if node.has_elif_block(): + ifs.append("el%s" % self._stmt_list(node.orelse, indent=False)) + elif node.orelse: + ifs.append("else:\n%s" % self._stmt_list(node.orelse)) + return "\n".join(ifs) + + def visit_ifexp(self, node): + """return an astroid.IfExp node as string""" + return "%s if %s else %s" % ( + self._precedence_parens(node, node.body, is_left=True), + self._precedence_parens(node, node.test, is_left=True), + self._precedence_parens(node, node.orelse, is_left=False), + ) + + def visit_import(self, node): + """return an astroid.Import node as string""" + return "import %s" % _import_string(node.names) + + def visit_keyword(self, node): + """return an astroid.Keyword node as string""" + if node.arg is None: + return "**%s" % node.value.accept(self) + return "%s=%s" % (node.arg, node.value.accept(self)) + + def visit_lambda(self, node): + """return an astroid.Lambda node as string""" + args = node.args.accept(self) + body = node.body.accept(self) + if args: + return "lambda %s: %s" % (args, body) + + return "lambda: %s" % body + + def visit_list(self, node): + """return an astroid.List node as string""" + return "[%s]" % ", ".join(child.accept(self) for child in node.elts) + + def visit_listcomp(self, node): + """return an astroid.ListComp node as string""" + return "[%s %s]" % ( + node.elt.accept(self), + " ".join(n.accept(self) for n in node.generators), + ) + + def visit_module(self, node): + """return an astroid.Module node as string""" + docs = '"""%s"""\n\n' % node.doc if node.doc else "" + return docs + "\n".join(n.accept(self) for n in node.body) + "\n\n" + + def visit_name(self, node): + """return an astroid.Name node as string""" + return node.name + + def visit_namedexpr(self, node): + """Return an assignment expression node as string""" + target = node.target.accept(self) + value = node.value.accept(self) + return "%s := %s" % (target, value) + + def visit_nonlocal(self, node): + """return an astroid.Nonlocal node as string""" + return "nonlocal %s" % ", ".join(node.names) + + def visit_pass(self, node): + """return an astroid.Pass node as string""" + return "pass" + + def visit_print(self, node): + """return an astroid.Print node as string""" + nodes = ", ".join(n.accept(self) for n in node.values) + if not node.nl: + nodes = "%s," % nodes + if node.dest: + return "print >> %s, %s" % (node.dest.accept(self), nodes) + return "print %s" % nodes + + def visit_raise(self, node): + """return an astroid.Raise node as string""" + if node.exc: + if node.cause: + return "raise %s from %s" % ( + node.exc.accept(self), + node.cause.accept(self), + ) + return "raise %s" % node.exc.accept(self) + return "raise" + + def visit_return(self, node): + """return an astroid.Return node as string""" + if node.is_tuple_return() and len(node.value.elts) > 1: + elts = [child.accept(self) for child in node.value.elts] + return "return %s" % ", ".join(elts) + + if node.value: + return "return %s" % node.value.accept(self) + + return "return" + + def visit_index(self, node): + """return an astroid.Index node as string""" + return node.value.accept(self) + + def visit_set(self, node): + """return an astroid.Set node as string""" + return "{%s}" % ", ".join(child.accept(self) for child in node.elts) + + def visit_setcomp(self, node): + """return an astroid.SetComp node as string""" + return "{%s %s}" % ( + node.elt.accept(self), + " ".join(n.accept(self) for n in node.generators), + ) + + def visit_slice(self, node): + """return an astroid.Slice node as string""" + lower = node.lower.accept(self) if node.lower else "" + upper = node.upper.accept(self) if node.upper else "" + step = node.step.accept(self) if node.step else "" + if step: + return "%s:%s:%s" % (lower, upper, step) + return "%s:%s" % (lower, upper) + + def visit_subscript(self, node): + """return an astroid.Subscript node as string""" + idx = node.slice + if idx.__class__.__name__.lower() == "index": + idx = idx.value + idxstr = idx.accept(self) + if idx.__class__.__name__.lower() == "tuple" and idx.elts: + # Remove parenthesis in tuple and extended slice. + # a[(::1, 1:)] is not valid syntax. + idxstr = idxstr[1:-1] + return "%s[%s]" % (self._precedence_parens(node, node.value), idxstr) + + def visit_tryexcept(self, node): + """return an astroid.TryExcept node as string""" + trys = ["try:\n%s" % self._stmt_list(node.body)] + for handler in node.handlers: + trys.append(handler.accept(self)) + if node.orelse: + trys.append("else:\n%s" % self._stmt_list(node.orelse)) + return "\n".join(trys) + + def visit_tryfinally(self, node): + """return an astroid.TryFinally node as string""" + return "try:\n%s\nfinally:\n%s" % ( + self._stmt_list(node.body), + self._stmt_list(node.finalbody), + ) + + def visit_tuple(self, node): + """return an astroid.Tuple node as string""" + if len(node.elts) == 1: + return "(%s, )" % node.elts[0].accept(self) + return "(%s)" % ", ".join(child.accept(self) for child in node.elts) + + def visit_unaryop(self, node): + """return an astroid.UnaryOp node as string""" + if node.op == "not": + operator = "not " + else: + operator = node.op + return "%s%s" % (operator, self._precedence_parens(node, node.operand)) + + def visit_while(self, node): + """return an astroid.While node as string""" + whiles = "while %s:\n%s" % (node.test.accept(self), self._stmt_list(node.body)) + if node.orelse: + whiles = "%s\nelse:\n%s" % (whiles, self._stmt_list(node.orelse)) + return whiles + + def visit_with(self, node): # 'with' without 'as' is possible + """return an astroid.With node as string""" + items = ", ".join( + ("%s" % expr.accept(self)) + (vars and " as %s" % (vars.accept(self)) or "") + for expr, vars in node.items + ) + return "with %s:\n%s" % (items, self._stmt_list(node.body)) + + def visit_yield(self, node): + """yield an ast.Yield node as string""" + yi_val = (" " + node.value.accept(self)) if node.value else "" + expr = "yield" + yi_val + if node.parent.is_statement: + return expr + + return "(%s)" % (expr,) + + def visit_yieldfrom(self, node): + """ Return an astroid.YieldFrom node as string. """ + yi_val = (" " + node.value.accept(self)) if node.value else "" + expr = "yield from" + yi_val + if node.parent.is_statement: + return expr + + return "(%s)" % (expr,) + + def visit_starred(self, node): + """return Starred node as string""" + return "*" + node.value.accept(self) + + # These aren't for real AST nodes, but for inference objects. + + def visit_frozenset(self, node): + return node.parent.accept(self) + + def visit_super(self, node): + return node.parent.accept(self) + + def visit_uninferable(self, node): + return str(node) + + def visit_property(self, node): + return node.function.accept(self) + + def visit_evaluatedobject(self, node): + return node.original.accept(self) + + +def _import_string(names): + """return a list of (name, asname) formatted as a string""" + _names = [] + for name, asname in names: + if asname is not None: + _names.append("%s as %s" % (name, asname)) + else: + _names.append(name) + return ", ".join(_names) + + +# This sets the default indent to 4 spaces. +to_code = AsStringVisitor(" ") diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/bases.py b/Display/.venv/lib/python3.7/site-packages/astroid/bases.py new file mode 100644 index 0000000..9c74303 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/bases.py @@ -0,0 +1,548 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2012 FELD Boris +# Copyright (c) 2014-2020 Claudiu Popa +# Copyright (c) 2014 Google, Inc. +# Copyright (c) 2014 Eevee (Alex Munroe) +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2015 Florian Bruhin +# Copyright (c) 2016-2017 Derek Gustafson +# Copyright (c) 2017 Calen Pennington +# Copyright (c) 2018-2019 hippo91 +# Copyright (c) 2018 Ville Skyttä +# Copyright (c) 2018 Bryce Guinta +# Copyright (c) 2018 Nick Drozd +# Copyright (c) 2018 Daniel Colascione +# Copyright (c) 2019 Hugo van Kemenade + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""This module contains base classes and functions for the nodes and some +inference utils. +""" + +import builtins +import collections + +from astroid import context as contextmod +from astroid import exceptions +from astroid import util + +objectmodel = util.lazy_import("interpreter.objectmodel") +helpers = util.lazy_import("helpers") +BUILTINS = builtins.__name__ +manager = util.lazy_import("manager") +MANAGER = manager.AstroidManager() + +# TODO: check if needs special treatment +BUILTINS = "builtins" +BOOL_SPECIAL_METHOD = "__bool__" + +PROPERTIES = {BUILTINS + ".property", "abc.abstractproperty"} +# List of possible property names. We use this list in order +# to see if a method is a property or not. This should be +# pretty reliable and fast, the alternative being to check each +# decorator to see if its a real property-like descriptor, which +# can be too complicated. +# Also, these aren't qualified, because each project can +# define them, we shouldn't expect to know every possible +# property-like decorator! +POSSIBLE_PROPERTIES = { + "cached_property", + "cachedproperty", + "lazyproperty", + "lazy_property", + "reify", + "lazyattribute", + "lazy_attribute", + "LazyProperty", + "lazy", + "cache_readonly", +} + + +def _is_property(meth, context=None): + decoratornames = meth.decoratornames(context=context) + if PROPERTIES.intersection(decoratornames): + return True + stripped = { + name.split(".")[-1] for name in decoratornames if name is not util.Uninferable + } + if any(name in stripped for name in POSSIBLE_PROPERTIES): + return True + + # Lookup for subclasses of *property* + if not meth.decorators: + return False + for decorator in meth.decorators.nodes or (): + inferred = helpers.safe_infer(decorator, context=context) + if inferred is None or inferred is util.Uninferable: + continue + if inferred.__class__.__name__ == "ClassDef": + for base_class in inferred.bases: + if base_class.__class__.__name__ != "Name": + continue + module, _ = base_class.lookup(base_class.name) + if module.name == BUILTINS and base_class.name == "property": + return True + + return False + + +class Proxy: + """a simple proxy object + + Note: + + Subclasses of this object will need a custom __getattr__ + if new instance attributes are created. See the Const class + """ + + _proxied = None # proxied object may be set by class or by instance + + def __init__(self, proxied=None): + if proxied is not None: + self._proxied = proxied + + def __getattr__(self, name): + if name == "_proxied": + return getattr(self.__class__, "_proxied") + if name in self.__dict__: + return self.__dict__[name] + return getattr(self._proxied, name) + + def infer(self, context=None): + yield self + + +def _infer_stmts(stmts, context, frame=None): + """Return an iterator on statements inferred by each statement in *stmts*.""" + inferred = False + if context is not None: + name = context.lookupname + context = context.clone() + else: + name = None + context = contextmod.InferenceContext() + + for stmt in stmts: + if stmt is util.Uninferable: + yield stmt + inferred = True + continue + context.lookupname = stmt._infer_name(frame, name) + try: + for inferred in stmt.infer(context=context): + yield inferred + inferred = True + except exceptions.NameInferenceError: + continue + except exceptions.InferenceError: + yield util.Uninferable + inferred = True + if not inferred: + raise exceptions.InferenceError( + "Inference failed for all members of {stmts!r}.", + stmts=stmts, + frame=frame, + context=context, + ) + + +def _infer_method_result_truth(instance, method_name, context): + # Get the method from the instance and try to infer + # its return's truth value. + meth = next(instance.igetattr(method_name, context=context), None) + if meth and hasattr(meth, "infer_call_result"): + if not meth.callable(): + return util.Uninferable + try: + for value in meth.infer_call_result(instance, context=context): + if value is util.Uninferable: + return value + + inferred = next(value.infer(context=context)) + return inferred.bool_value() + except exceptions.InferenceError: + pass + return util.Uninferable + + +class BaseInstance(Proxy): + """An instance base class, which provides lookup methods for potential instances.""" + + special_attributes = None + + def display_type(self): + return "Instance of" + + def getattr(self, name, context=None, lookupclass=True): + try: + values = self._proxied.instance_attr(name, context) + except exceptions.AttributeInferenceError as exc: + if self.special_attributes and name in self.special_attributes: + return [self.special_attributes.lookup(name)] + + if lookupclass: + # Class attributes not available through the instance + # unless they are explicitly defined. + return self._proxied.getattr(name, context, class_context=False) + + raise exceptions.AttributeInferenceError( + target=self, attribute=name, context=context + ) from exc + # since we've no context information, return matching class members as + # well + if lookupclass: + try: + return values + self._proxied.getattr( + name, context, class_context=False + ) + except exceptions.AttributeInferenceError: + pass + return values + + def igetattr(self, name, context=None): + """inferred getattr""" + if not context: + context = contextmod.InferenceContext() + try: + # avoid recursively inferring the same attr on the same class + if context.push((self._proxied, name)): + raise exceptions.InferenceError( + message="Cannot infer the same attribute again", + node=self, + context=context, + ) + + # XXX frame should be self._proxied, or not ? + get_attr = self.getattr(name, context, lookupclass=False) + yield from _infer_stmts( + self._wrap_attr(get_attr, context), context, frame=self + ) + except exceptions.AttributeInferenceError as error: + try: + # fallback to class.igetattr since it has some logic to handle + # descriptors + # But only if the _proxied is the Class. + if self._proxied.__class__.__name__ != "ClassDef": + raise + attrs = self._proxied.igetattr(name, context, class_context=False) + yield from self._wrap_attr(attrs, context) + except exceptions.AttributeInferenceError as error: + raise exceptions.InferenceError(**vars(error)) from error + + def _wrap_attr(self, attrs, context=None): + """wrap bound methods of attrs in a InstanceMethod proxies""" + for attr in attrs: + if isinstance(attr, UnboundMethod): + if _is_property(attr): + yield from attr.infer_call_result(self, context) + else: + yield BoundMethod(attr, self) + elif hasattr(attr, "name") and attr.name == "": + if attr.args.arguments and attr.args.arguments[0].name == "self": + yield BoundMethod(attr, self) + continue + yield attr + else: + yield attr + + def infer_call_result(self, caller, context=None): + """infer what a class instance is returning when called""" + context = contextmod.bind_context_to_node(context, self) + inferred = False + for node in self._proxied.igetattr("__call__", context): + if node is util.Uninferable or not node.callable(): + continue + for res in node.infer_call_result(caller, context): + inferred = True + yield res + if not inferred: + raise exceptions.InferenceError(node=self, caller=caller, context=context) + + +class Instance(BaseInstance): + """A special node representing a class instance.""" + + # pylint: disable=unnecessary-lambda + special_attributes = util.lazy_descriptor(lambda: objectmodel.InstanceModel()) + + def __repr__(self): + return "" % ( + self._proxied.root().name, + self._proxied.name, + id(self), + ) + + def __str__(self): + return "Instance of %s.%s" % (self._proxied.root().name, self._proxied.name) + + def callable(self): + try: + self._proxied.getattr("__call__", class_context=False) + return True + except exceptions.AttributeInferenceError: + return False + + def pytype(self): + return self._proxied.qname() + + def display_type(self): + return "Instance of" + + def bool_value(self, context=None): + """Infer the truth value for an Instance + + The truth value of an instance is determined by these conditions: + + * if it implements __bool__ on Python 3 or __nonzero__ + on Python 2, then its bool value will be determined by + calling this special method and checking its result. + * when this method is not defined, __len__() is called, if it + is defined, and the object is considered true if its result is + nonzero. If a class defines neither __len__() nor __bool__(), + all its instances are considered true. + """ + context = context or contextmod.InferenceContext() + context.callcontext = contextmod.CallContext(args=[]) + context.boundnode = self + + try: + result = _infer_method_result_truth(self, BOOL_SPECIAL_METHOD, context) + except (exceptions.InferenceError, exceptions.AttributeInferenceError): + # Fallback to __len__. + try: + result = _infer_method_result_truth(self, "__len__", context) + except (exceptions.AttributeInferenceError, exceptions.InferenceError): + return True + return result + + # This is set in inference.py. + def getitem(self, index, context=None): + pass + + +class UnboundMethod(Proxy): + """a special node representing a method not bound to an instance""" + + # pylint: disable=unnecessary-lambda + special_attributes = util.lazy_descriptor(lambda: objectmodel.UnboundMethodModel()) + + def __repr__(self): + frame = self._proxied.parent.frame() + return "<%s %s of %s at 0x%s" % ( + self.__class__.__name__, + self._proxied.name, + frame.qname(), + id(self), + ) + + def implicit_parameters(self): + return 0 + + def is_bound(self): + return False + + def getattr(self, name, context=None): + if name in self.special_attributes: + return [self.special_attributes.lookup(name)] + return self._proxied.getattr(name, context) + + def igetattr(self, name, context=None): + if name in self.special_attributes: + return iter((self.special_attributes.lookup(name),)) + return self._proxied.igetattr(name, context) + + def infer_call_result(self, caller, context): + """ + The boundnode of the regular context with a function called + on ``object.__new__`` will be of type ``object``, + which is incorrect for the argument in general. + If no context is given the ``object.__new__`` call argument will + correctly inferred except when inside a call that requires + the additional context (such as a classmethod) of the boundnode + to determine which class the method was called from + """ + + # If we're unbound method __new__ of builtin object, the result is an + # instance of the class given as first argument. + if ( + self._proxied.name == "__new__" + and self._proxied.parent.frame().qname() == "%s.object" % BUILTINS + ): + if caller.args: + node_context = context.extra_context.get(caller.args[0]) + infer = caller.args[0].infer(context=node_context) + else: + infer = [] + return (Instance(x) if x is not util.Uninferable else x for x in infer) + return self._proxied.infer_call_result(caller, context) + + def bool_value(self, context=None): + return True + + +class BoundMethod(UnboundMethod): + """a special node representing a method bound to an instance""" + + # pylint: disable=unnecessary-lambda + special_attributes = util.lazy_descriptor(lambda: objectmodel.BoundMethodModel()) + + def __init__(self, proxy, bound): + UnboundMethod.__init__(self, proxy) + self.bound = bound + + def implicit_parameters(self): + if self.name == "__new__": + # __new__ acts as a classmethod but the class argument is not implicit. + return 0 + return 1 + + def is_bound(self): + return True + + def _infer_type_new_call(self, caller, context): + """Try to infer what type.__new__(mcs, name, bases, attrs) returns. + + In order for such call to be valid, the metaclass needs to be + a subtype of ``type``, the name needs to be a string, the bases + needs to be a tuple of classes + """ + # pylint: disable=import-outside-toplevel; circular import + from astroid import node_classes + + # Verify the metaclass + mcs = next(caller.args[0].infer(context=context)) + if mcs.__class__.__name__ != "ClassDef": + # Not a valid first argument. + return None + if not mcs.is_subtype_of("%s.type" % BUILTINS): + # Not a valid metaclass. + return None + + # Verify the name + name = next(caller.args[1].infer(context=context)) + if name.__class__.__name__ != "Const": + # Not a valid name, needs to be a const. + return None + if not isinstance(name.value, str): + # Needs to be a string. + return None + + # Verify the bases + bases = next(caller.args[2].infer(context=context)) + if bases.__class__.__name__ != "Tuple": + # Needs to be a tuple. + return None + inferred_bases = [next(elt.infer(context=context)) for elt in bases.elts] + if any(base.__class__.__name__ != "ClassDef" for base in inferred_bases): + # All the bases needs to be Classes + return None + + # Verify the attributes. + attrs = next(caller.args[3].infer(context=context)) + if attrs.__class__.__name__ != "Dict": + # Needs to be a dictionary. + return None + cls_locals = collections.defaultdict(list) + for key, value in attrs.items: + key = next(key.infer(context=context)) + value = next(value.infer(context=context)) + # Ignore non string keys + if key.__class__.__name__ == "Const" and isinstance(key.value, str): + cls_locals[key.value].append(value) + + # Build the class from now. + cls = mcs.__class__( + name=name.value, + lineno=caller.lineno, + col_offset=caller.col_offset, + parent=caller, + ) + empty = node_classes.Pass() + cls.postinit( + bases=bases.elts, + body=[empty], + decorators=[], + newstyle=True, + metaclass=mcs, + keywords=[], + ) + cls.locals = cls_locals + return cls + + def infer_call_result(self, caller, context=None): + context = contextmod.bind_context_to_node(context, self.bound) + if ( + self.bound.__class__.__name__ == "ClassDef" + and self.bound.name == "type" + and self.name == "__new__" + and len(caller.args) == 4 + ): + # Check if we have a ``type.__new__(mcs, name, bases, attrs)`` call. + new_cls = self._infer_type_new_call(caller, context) + if new_cls: + return iter((new_cls,)) + + return super().infer_call_result(caller, context) + + def bool_value(self, context=None): + return True + + +class Generator(BaseInstance): + """a special node representing a generator. + + Proxied class is set once for all in raw_building. + """ + + # pylint: disable=unnecessary-lambda + special_attributes = util.lazy_descriptor(lambda: objectmodel.GeneratorModel()) + + # pylint: disable=super-init-not-called + def __init__(self, parent=None): + self.parent = parent + + def callable(self): + return False + + def pytype(self): + return "%s.generator" % BUILTINS + + def display_type(self): + return "Generator" + + def bool_value(self, context=None): + return True + + def __repr__(self): + return "" % ( + self._proxied.name, + self.lineno, + id(self), + ) + + def __str__(self): + return "Generator(%s)" % (self._proxied.name) + + +class AsyncGenerator(Generator): + """Special node representing an async generator""" + + def pytype(self): + return "%s.async_generator" % BUILTINS + + def display_type(self): + return "AsyncGenerator" + + def __repr__(self): + return "" % ( + self._proxied.name, + self.lineno, + id(self), + ) + + def __str__(self): + return "AsyncGenerator(%s)" % (self._proxied.name) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_argparse.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_argparse.py new file mode 100644 index 0000000..6a7556f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_argparse.py @@ -0,0 +1,33 @@ +from astroid import MANAGER, arguments, nodes, inference_tip, UseInferenceDefault + + +def infer_namespace(node, context=None): + callsite = arguments.CallSite.from_call(node, context=context) + if not callsite.keyword_arguments: + # Cannot make sense of it. + raise UseInferenceDefault() + + class_node = nodes.ClassDef("Namespace", "docstring") + class_node.parent = node.parent + for attr in set(callsite.keyword_arguments): + fake_node = nodes.EmptyNode() + fake_node.parent = class_node + fake_node.attrname = attr + class_node.instance_attrs[attr] = [fake_node] + return iter((class_node.instantiate_class(),)) + + +def _looks_like_namespace(node): + func = node.func + if isinstance(func, nodes.Attribute): + return ( + func.attrname == "Namespace" + and isinstance(func.expr, nodes.Name) + and func.expr.name == "argparse" + ) + return False + + +MANAGER.register_transform( + nodes.Call, inference_tip(infer_namespace), _looks_like_namespace +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_attrs.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_attrs.py new file mode 100644 index 0000000..670736f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_attrs.py @@ -0,0 +1,65 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER +""" +Astroid hook for the attrs library + +Without this hook pylint reports unsupported-assignment-operation +for attrs classes +""" + +import astroid +from astroid import MANAGER + + +ATTRIB_NAMES = frozenset(("attr.ib", "attrib", "attr.attrib")) +ATTRS_NAMES = frozenset(("attr.s", "attrs", "attr.attrs", "attr.attributes")) + + +def is_decorated_with_attrs(node, decorator_names=ATTRS_NAMES): + """Return True if a decorated node has + an attr decorator applied.""" + if not node.decorators: + return False + for decorator_attribute in node.decorators.nodes: + if isinstance(decorator_attribute, astroid.Call): # decorator with arguments + decorator_attribute = decorator_attribute.func + if decorator_attribute.as_string() in decorator_names: + return True + return False + + +def attr_attributes_transform(node): + """Given that the ClassNode has an attr decorator, + rewrite class attributes as instance attributes + """ + # Astroid can't infer this attribute properly + # Prevents https://github.com/PyCQA/pylint/issues/1884 + node.locals["__attrs_attrs__"] = [astroid.Unknown(parent=node)] + + for cdefbodynode in node.body: + if not isinstance(cdefbodynode, (astroid.Assign, astroid.AnnAssign)): + continue + if isinstance(cdefbodynode.value, astroid.Call): + if cdefbodynode.value.func.as_string() not in ATTRIB_NAMES: + continue + else: + continue + targets = ( + cdefbodynode.targets + if hasattr(cdefbodynode, "targets") + else [cdefbodynode.target] + ) + for target in targets: + + rhs_node = astroid.Unknown( + lineno=cdefbodynode.lineno, + col_offset=cdefbodynode.col_offset, + parent=cdefbodynode, + ) + node.locals[target.name] = [rhs_node] + node.instance_attrs[target.name] = [rhs_node] + + +MANAGER.register_transform( + astroid.ClassDef, attr_attributes_transform, is_decorated_with_attrs +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_boto3.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_boto3.py new file mode 100644 index 0000000..342ca57 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_boto3.py @@ -0,0 +1,28 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""Astroid hooks for understanding boto3.ServiceRequest()""" +import astroid +from astroid import MANAGER, extract_node + +BOTO_SERVICE_FACTORY_QUALIFIED_NAME = "boto3.resources.base.ServiceResource" + + +def service_request_transform(node): + """Transform ServiceResource to look like dynamic classes""" + code = """ + def __getattr__(self, attr): + return 0 + """ + func_getattr = extract_node(code) + node.locals["__getattr__"] = [func_getattr] + return node + + +def _looks_like_boto3_service_request(node): + return node.qname() == BOTO_SERVICE_FACTORY_QUALIFIED_NAME + + +MANAGER.register_transform( + astroid.ClassDef, service_request_transform, _looks_like_boto3_service_request +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_builtin_inference.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_builtin_inference.py new file mode 100644 index 0000000..4b07ac5 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_builtin_inference.py @@ -0,0 +1,873 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2014-2020 Claudiu Popa +# Copyright (c) 2014-2015 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2015 Rene Zhang +# Copyright (c) 2018 Bryce Guinta +# Copyright (c) 2018 Ville Skyttä +# Copyright (c) 2019 Stanislav Levin +# Copyright (c) 2019 David Liu +# Copyright (c) 2019 Bryce Guinta +# Copyright (c) 2019 Frédéric Chapoton + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""Astroid hooks for various builtins.""" + +from functools import partial +from textwrap import dedent + +import six +from astroid import ( + MANAGER, + UseInferenceDefault, + AttributeInferenceError, + inference_tip, + InferenceError, + NameInferenceError, + AstroidTypeError, + MroError, +) +from astroid import arguments +from astroid.builder import AstroidBuilder +from astroid import helpers +from astroid import nodes +from astroid import objects +from astroid import scoped_nodes +from astroid import util + + +OBJECT_DUNDER_NEW = "object.__new__" + + +def _extend_str(class_node, rvalue): + """function to extend builtin str/unicode class""" + code = dedent( + """ + class whatever(object): + def join(self, iterable): + return {rvalue} + def replace(self, old, new, count=None): + return {rvalue} + def format(self, *args, **kwargs): + return {rvalue} + def encode(self, encoding='ascii', errors=None): + return '' + def decode(self, encoding='ascii', errors=None): + return u'' + def capitalize(self): + return {rvalue} + def title(self): + return {rvalue} + def lower(self): + return {rvalue} + def upper(self): + return {rvalue} + def swapcase(self): + return {rvalue} + def index(self, sub, start=None, end=None): + return 0 + def find(self, sub, start=None, end=None): + return 0 + def count(self, sub, start=None, end=None): + return 0 + def strip(self, chars=None): + return {rvalue} + def lstrip(self, chars=None): + return {rvalue} + def rstrip(self, chars=None): + return {rvalue} + def rjust(self, width, fillchar=None): + return {rvalue} + def center(self, width, fillchar=None): + return {rvalue} + def ljust(self, width, fillchar=None): + return {rvalue} + """ + ) + code = code.format(rvalue=rvalue) + fake = AstroidBuilder(MANAGER).string_build(code)["whatever"] + for method in fake.mymethods(): + method.parent = class_node + method.lineno = None + method.col_offset = None + if "__class__" in method.locals: + method.locals["__class__"] = [class_node] + class_node.locals[method.name] = [method] + method.parent = class_node + + +def _extend_builtins(class_transforms): + builtin_ast = MANAGER.builtins_module + for class_name, transform in class_transforms.items(): + transform(builtin_ast[class_name]) + + +_extend_builtins( + { + "bytes": partial(_extend_str, rvalue="b''"), + "str": partial(_extend_str, rvalue="''"), + } +) + + +def _builtin_filter_predicate(node, builtin_name): + if isinstance(node.func, nodes.Name) and node.func.name == builtin_name: + return True + if isinstance(node.func, nodes.Attribute): + return ( + node.func.attrname == "fromkeys" + and isinstance(node.func.expr, nodes.Name) + and node.func.expr.name == "dict" + ) + return False + + +def register_builtin_transform(transform, builtin_name): + """Register a new transform function for the given *builtin_name*. + + The transform function must accept two parameters, a node and + an optional context. + """ + + def _transform_wrapper(node, context=None): + result = transform(node, context=context) + if result: + if not result.parent: + # Let the transformation function determine + # the parent for its result. Otherwise, + # we set it to be the node we transformed from. + result.parent = node + + if result.lineno is None: + result.lineno = node.lineno + if result.col_offset is None: + result.col_offset = node.col_offset + return iter([result]) + + MANAGER.register_transform( + nodes.Call, + inference_tip(_transform_wrapper), + partial(_builtin_filter_predicate, builtin_name=builtin_name), + ) + + +def _container_generic_inference(node, context, node_type, transform): + args = node.args + if not args: + return node_type() + if len(node.args) > 1: + raise UseInferenceDefault() + + (arg,) = args + transformed = transform(arg) + if not transformed: + try: + inferred = next(arg.infer(context=context)) + except (InferenceError, StopIteration): + raise UseInferenceDefault() + if inferred is util.Uninferable: + raise UseInferenceDefault() + transformed = transform(inferred) + if not transformed or transformed is util.Uninferable: + raise UseInferenceDefault() + return transformed + + +def _container_generic_transform(arg, context, klass, iterables, build_elts): + if isinstance(arg, klass): + return arg + elif isinstance(arg, iterables): + if all(isinstance(elt, nodes.Const) for elt in arg.elts): + elts = [elt.value for elt in arg.elts] + else: + # TODO: Does not handle deduplication for sets. + elts = [] + for element in arg.elts: + inferred = helpers.safe_infer(element, context=context) + if inferred: + evaluated_object = nodes.EvaluatedObject( + original=element, value=inferred + ) + elts.append(evaluated_object) + elif isinstance(arg, nodes.Dict): + # Dicts need to have consts as strings already. + if not all(isinstance(elt[0], nodes.Const) for elt in arg.items): + raise UseInferenceDefault() + elts = [item[0].value for item in arg.items] + elif isinstance(arg, nodes.Const) and isinstance( + arg.value, (six.string_types, six.binary_type) + ): + elts = arg.value + else: + return + return klass.from_elements(elts=build_elts(elts)) + + +def _infer_builtin_container( + node, context, klass=None, iterables=None, build_elts=None +): + transform_func = partial( + _container_generic_transform, + context=context, + klass=klass, + iterables=iterables, + build_elts=build_elts, + ) + + return _container_generic_inference(node, context, klass, transform_func) + + +# pylint: disable=invalid-name +infer_tuple = partial( + _infer_builtin_container, + klass=nodes.Tuple, + iterables=( + nodes.List, + nodes.Set, + objects.FrozenSet, + objects.DictItems, + objects.DictKeys, + objects.DictValues, + ), + build_elts=tuple, +) + +infer_list = partial( + _infer_builtin_container, + klass=nodes.List, + iterables=( + nodes.Tuple, + nodes.Set, + objects.FrozenSet, + objects.DictItems, + objects.DictKeys, + objects.DictValues, + ), + build_elts=list, +) + +infer_set = partial( + _infer_builtin_container, + klass=nodes.Set, + iterables=(nodes.List, nodes.Tuple, objects.FrozenSet, objects.DictKeys), + build_elts=set, +) + +infer_frozenset = partial( + _infer_builtin_container, + klass=objects.FrozenSet, + iterables=(nodes.List, nodes.Tuple, nodes.Set, objects.FrozenSet, objects.DictKeys), + build_elts=frozenset, +) + + +def _get_elts(arg, context): + is_iterable = lambda n: isinstance(n, (nodes.List, nodes.Tuple, nodes.Set)) + try: + inferred = next(arg.infer(context)) + except (InferenceError, NameInferenceError): + raise UseInferenceDefault() + if isinstance(inferred, nodes.Dict): + items = inferred.items + elif is_iterable(inferred): + items = [] + for elt in inferred.elts: + # If an item is not a pair of two items, + # then fallback to the default inference. + # Also, take in consideration only hashable items, + # tuples and consts. We are choosing Names as well. + if not is_iterable(elt): + raise UseInferenceDefault() + if len(elt.elts) != 2: + raise UseInferenceDefault() + if not isinstance(elt.elts[0], (nodes.Tuple, nodes.Const, nodes.Name)): + raise UseInferenceDefault() + items.append(tuple(elt.elts)) + else: + raise UseInferenceDefault() + return items + + +def infer_dict(node, context=None): + """Try to infer a dict call to a Dict node. + + The function treats the following cases: + + * dict() + * dict(mapping) + * dict(iterable) + * dict(iterable, **kwargs) + * dict(mapping, **kwargs) + * dict(**kwargs) + + If a case can't be inferred, we'll fallback to default inference. + """ + call = arguments.CallSite.from_call(node, context=context) + if call.has_invalid_arguments() or call.has_invalid_keywords(): + raise UseInferenceDefault + + args = call.positional_arguments + kwargs = list(call.keyword_arguments.items()) + + if not args and not kwargs: + # dict() + return nodes.Dict() + elif kwargs and not args: + # dict(a=1, b=2, c=4) + items = [(nodes.Const(key), value) for key, value in kwargs] + elif len(args) == 1 and kwargs: + # dict(some_iterable, b=2, c=4) + elts = _get_elts(args[0], context) + keys = [(nodes.Const(key), value) for key, value in kwargs] + items = elts + keys + elif len(args) == 1: + items = _get_elts(args[0], context) + else: + raise UseInferenceDefault() + + value = nodes.Dict( + col_offset=node.col_offset, lineno=node.lineno, parent=node.parent + ) + value.postinit(items) + return value + + +def infer_super(node, context=None): + """Understand super calls. + + There are some restrictions for what can be understood: + + * unbounded super (one argument form) is not understood. + + * if the super call is not inside a function (classmethod or method), + then the default inference will be used. + + * if the super arguments can't be inferred, the default inference + will be used. + """ + if len(node.args) == 1: + # Ignore unbounded super. + raise UseInferenceDefault + + scope = node.scope() + if not isinstance(scope, nodes.FunctionDef): + # Ignore non-method uses of super. + raise UseInferenceDefault + if scope.type not in ("classmethod", "method"): + # Not interested in staticmethods. + raise UseInferenceDefault + + cls = scoped_nodes.get_wrapping_class(scope) + if not len(node.args): + mro_pointer = cls + # In we are in a classmethod, the interpreter will fill + # automatically the class as the second argument, not an instance. + if scope.type == "classmethod": + mro_type = cls + else: + mro_type = cls.instantiate_class() + else: + try: + mro_pointer = next(node.args[0].infer(context=context)) + except InferenceError: + raise UseInferenceDefault + try: + mro_type = next(node.args[1].infer(context=context)) + except InferenceError: + raise UseInferenceDefault + + if mro_pointer is util.Uninferable or mro_type is util.Uninferable: + # No way we could understand this. + raise UseInferenceDefault + + super_obj = objects.Super( + mro_pointer=mro_pointer, mro_type=mro_type, self_class=cls, scope=scope + ) + super_obj.parent = node + return super_obj + + +def _infer_getattr_args(node, context): + if len(node.args) not in (2, 3): + # Not a valid getattr call. + raise UseInferenceDefault + + try: + obj = next(node.args[0].infer(context=context)) + attr = next(node.args[1].infer(context=context)) + except InferenceError: + raise UseInferenceDefault + + if obj is util.Uninferable or attr is util.Uninferable: + # If one of the arguments is something we can't infer, + # then also make the result of the getattr call something + # which is unknown. + return util.Uninferable, util.Uninferable + + is_string = isinstance(attr, nodes.Const) and isinstance( + attr.value, six.string_types + ) + if not is_string: + raise UseInferenceDefault + + return obj, attr.value + + +def infer_getattr(node, context=None): + """Understand getattr calls + + If one of the arguments is an Uninferable object, then the + result will be an Uninferable object. Otherwise, the normal attribute + lookup will be done. + """ + obj, attr = _infer_getattr_args(node, context) + if ( + obj is util.Uninferable + or attr is util.Uninferable + or not hasattr(obj, "igetattr") + ): + return util.Uninferable + + try: + return next(obj.igetattr(attr, context=context)) + except (StopIteration, InferenceError, AttributeInferenceError): + if len(node.args) == 3: + # Try to infer the default and return it instead. + try: + return next(node.args[2].infer(context=context)) + except InferenceError: + raise UseInferenceDefault + + raise UseInferenceDefault + + +def infer_hasattr(node, context=None): + """Understand hasattr calls + + This always guarantees three possible outcomes for calling + hasattr: Const(False) when we are sure that the object + doesn't have the intended attribute, Const(True) when + we know that the object has the attribute and Uninferable + when we are unsure of the outcome of the function call. + """ + try: + obj, attr = _infer_getattr_args(node, context) + if ( + obj is util.Uninferable + or attr is util.Uninferable + or not hasattr(obj, "getattr") + ): + return util.Uninferable + obj.getattr(attr, context=context) + except UseInferenceDefault: + # Can't infer something from this function call. + return util.Uninferable + except AttributeInferenceError: + # Doesn't have it. + return nodes.Const(False) + return nodes.Const(True) + + +def infer_callable(node, context=None): + """Understand callable calls + + This follows Python's semantics, where an object + is callable if it provides an attribute __call__, + even though that attribute is something which can't be + called. + """ + if len(node.args) != 1: + # Invalid callable call. + raise UseInferenceDefault + + argument = node.args[0] + try: + inferred = next(argument.infer(context=context)) + except InferenceError: + return util.Uninferable + if inferred is util.Uninferable: + return util.Uninferable + return nodes.Const(inferred.callable()) + + +def infer_property(node, context=None): + """Understand `property` class + + This only infers the output of `property` + call, not the arguments themselves. + """ + if len(node.args) < 1: + # Invalid property call. + raise UseInferenceDefault + + getter = node.args[0] + try: + inferred = next(getter.infer(context=context)) + except InferenceError: + raise UseInferenceDefault + + if not isinstance(inferred, (nodes.FunctionDef, nodes.Lambda)): + raise UseInferenceDefault + + return objects.Property( + function=inferred, + name=inferred.name, + doc=getattr(inferred, "doc", None), + lineno=node.lineno, + parent=node, + col_offset=node.col_offset, + ) + + +def infer_bool(node, context=None): + """Understand bool calls.""" + if len(node.args) > 1: + # Invalid bool call. + raise UseInferenceDefault + + if not node.args: + return nodes.Const(False) + + argument = node.args[0] + try: + inferred = next(argument.infer(context=context)) + except InferenceError: + return util.Uninferable + if inferred is util.Uninferable: + return util.Uninferable + + bool_value = inferred.bool_value(context=context) + if bool_value is util.Uninferable: + return util.Uninferable + return nodes.Const(bool_value) + + +def infer_type(node, context=None): + """Understand the one-argument form of *type*.""" + if len(node.args) != 1: + raise UseInferenceDefault + + return helpers.object_type(node.args[0], context) + + +def infer_slice(node, context=None): + """Understand `slice` calls.""" + args = node.args + if not 0 < len(args) <= 3: + raise UseInferenceDefault + + infer_func = partial(helpers.safe_infer, context=context) + args = [infer_func(arg) for arg in args] + for arg in args: + if not arg or arg is util.Uninferable: + raise UseInferenceDefault + if not isinstance(arg, nodes.Const): + raise UseInferenceDefault + if not isinstance(arg.value, (type(None), int)): + raise UseInferenceDefault + + if len(args) < 3: + # Make sure we have 3 arguments. + args.extend([None] * (3 - len(args))) + + slice_node = nodes.Slice( + lineno=node.lineno, col_offset=node.col_offset, parent=node.parent + ) + slice_node.postinit(*args) + return slice_node + + +def _infer_object__new__decorator(node, context=None): + # Instantiate class immediately + # since that's what @object.__new__ does + return iter((node.instantiate_class(),)) + + +def _infer_object__new__decorator_check(node): + """Predicate before inference_tip + + Check if the given ClassDef has an @object.__new__ decorator + """ + if not node.decorators: + return False + + for decorator in node.decorators.nodes: + if isinstance(decorator, nodes.Attribute): + if decorator.as_string() == OBJECT_DUNDER_NEW: + return True + return False + + +def infer_issubclass(callnode, context=None): + """Infer issubclass() calls + + :param nodes.Call callnode: an `issubclass` call + :param InferenceContext: the context for the inference + :rtype nodes.Const: Boolean Const value of the `issubclass` call + :raises UseInferenceDefault: If the node cannot be inferred + """ + call = arguments.CallSite.from_call(callnode, context=context) + if call.keyword_arguments: + # issubclass doesn't support keyword arguments + raise UseInferenceDefault("TypeError: issubclass() takes no keyword arguments") + if len(call.positional_arguments) != 2: + raise UseInferenceDefault( + "Expected two arguments, got {count}".format( + count=len(call.positional_arguments) + ) + ) + # The left hand argument is the obj to be checked + obj_node, class_or_tuple_node = call.positional_arguments + + try: + obj_type = next(obj_node.infer(context=context)) + except InferenceError as exc: + raise UseInferenceDefault from exc + if not isinstance(obj_type, nodes.ClassDef): + raise UseInferenceDefault("TypeError: arg 1 must be class") + + # The right hand argument is the class(es) that the given + # object is to be checked against. + try: + class_container = _class_or_tuple_to_container( + class_or_tuple_node, context=context + ) + except InferenceError as exc: + raise UseInferenceDefault from exc + try: + issubclass_bool = helpers.object_issubclass(obj_type, class_container, context) + except AstroidTypeError as exc: + raise UseInferenceDefault("TypeError: " + str(exc)) from exc + except MroError as exc: + raise UseInferenceDefault from exc + return nodes.Const(issubclass_bool) + + +def infer_isinstance(callnode, context=None): + """Infer isinstance calls + + :param nodes.Call callnode: an isinstance call + :param InferenceContext: context for call + (currently unused but is a common interface for inference) + :rtype nodes.Const: Boolean Const value of isinstance call + + :raises UseInferenceDefault: If the node cannot be inferred + """ + call = arguments.CallSite.from_call(callnode, context=context) + if call.keyword_arguments: + # isinstance doesn't support keyword arguments + raise UseInferenceDefault("TypeError: isinstance() takes no keyword arguments") + if len(call.positional_arguments) != 2: + raise UseInferenceDefault( + "Expected two arguments, got {count}".format( + count=len(call.positional_arguments) + ) + ) + # The left hand argument is the obj to be checked + obj_node, class_or_tuple_node = call.positional_arguments + # The right hand argument is the class(es) that the given + # obj is to be check is an instance of + try: + class_container = _class_or_tuple_to_container( + class_or_tuple_node, context=context + ) + except InferenceError: + raise UseInferenceDefault + try: + isinstance_bool = helpers.object_isinstance(obj_node, class_container, context) + except AstroidTypeError as exc: + raise UseInferenceDefault("TypeError: " + str(exc)) + except MroError as exc: + raise UseInferenceDefault from exc + if isinstance_bool is util.Uninferable: + raise UseInferenceDefault + return nodes.Const(isinstance_bool) + + +def _class_or_tuple_to_container(node, context=None): + # Move inferences results into container + # to simplify later logic + # raises InferenceError if any of the inferences fall through + node_infer = next(node.infer(context=context)) + # arg2 MUST be a type or a TUPLE of types + # for isinstance + if isinstance(node_infer, nodes.Tuple): + class_container = [ + next(node.infer(context=context)) for node in node_infer.elts + ] + class_container = [ + klass_node for klass_node in class_container if klass_node is not None + ] + else: + class_container = [node_infer] + return class_container + + +def infer_len(node, context=None): + """Infer length calls + + :param nodes.Call node: len call to infer + :param context.InferenceContext: node context + :rtype nodes.Const: a Const node with the inferred length, if possible + """ + call = arguments.CallSite.from_call(node, context=context) + if call.keyword_arguments: + raise UseInferenceDefault("TypeError: len() must take no keyword arguments") + if len(call.positional_arguments) != 1: + raise UseInferenceDefault( + "TypeError: len() must take exactly one argument " + "({len}) given".format(len=len(call.positional_arguments)) + ) + [argument_node] = call.positional_arguments + try: + return nodes.Const(helpers.object_len(argument_node, context=context)) + except (AstroidTypeError, InferenceError) as exc: + raise UseInferenceDefault(str(exc)) from exc + + +def infer_str(node, context=None): + """Infer str() calls + + :param nodes.Call node: str() call to infer + :param context.InferenceContext: node context + :rtype nodes.Const: a Const containing an empty string + """ + call = arguments.CallSite.from_call(node, context=context) + if call.keyword_arguments: + raise UseInferenceDefault("TypeError: str() must take no keyword arguments") + try: + return nodes.Const("") + except (AstroidTypeError, InferenceError) as exc: + raise UseInferenceDefault(str(exc)) from exc + + +def infer_int(node, context=None): + """Infer int() calls + + :param nodes.Call node: int() call to infer + :param context.InferenceContext: node context + :rtype nodes.Const: a Const containing the integer value of the int() call + """ + call = arguments.CallSite.from_call(node, context=context) + if call.keyword_arguments: + raise UseInferenceDefault("TypeError: int() must take no keyword arguments") + + if call.positional_arguments: + try: + first_value = next(call.positional_arguments[0].infer(context=context)) + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault(str(exc)) from exc + + if first_value is util.Uninferable: + raise UseInferenceDefault + + if isinstance(first_value, nodes.Const) and isinstance( + first_value.value, (int, str) + ): + try: + actual_value = int(first_value.value) + except ValueError: + return nodes.Const(0) + return nodes.Const(actual_value) + + return nodes.Const(0) + + +def infer_dict_fromkeys(node, context=None): + """Infer dict.fromkeys + + :param nodes.Call node: dict.fromkeys() call to infer + :param context.InferenceContext: node context + :rtype nodes.Dict: + a Dictionary containing the values that astroid was able to infer. + In case the inference failed for any reason, an empty dictionary + will be inferred instead. + """ + + def _build_dict_with_elements(elements): + new_node = nodes.Dict( + col_offset=node.col_offset, lineno=node.lineno, parent=node.parent + ) + new_node.postinit(elements) + return new_node + + call = arguments.CallSite.from_call(node, context=context) + if call.keyword_arguments: + raise UseInferenceDefault("TypeError: int() must take no keyword arguments") + if len(call.positional_arguments) not in {1, 2}: + raise UseInferenceDefault( + "TypeError: Needs between 1 and 2 positional arguments" + ) + + default = nodes.Const(None) + values = call.positional_arguments[0] + try: + inferred_values = next(values.infer(context=context)) + except InferenceError: + return _build_dict_with_elements([]) + if inferred_values is util.Uninferable: + return _build_dict_with_elements([]) + + # Limit to a couple of potential values, as this can become pretty complicated + accepted_iterable_elements = (nodes.Const,) + if isinstance(inferred_values, (nodes.List, nodes.Set, nodes.Tuple)): + elements = inferred_values.elts + for element in elements: + if not isinstance(element, accepted_iterable_elements): + # Fallback to an empty dict + return _build_dict_with_elements([]) + + elements_with_value = [(element, default) for element in elements] + return _build_dict_with_elements(elements_with_value) + + elif isinstance(inferred_values, nodes.Const) and isinstance( + inferred_values.value, (str, bytes) + ): + elements = [ + (nodes.Const(element), default) for element in inferred_values.value + ] + return _build_dict_with_elements(elements) + elif isinstance(inferred_values, nodes.Dict): + keys = inferred_values.itered() + for key in keys: + if not isinstance(key, accepted_iterable_elements): + # Fallback to an empty dict + return _build_dict_with_elements([]) + + elements_with_value = [(element, default) for element in keys] + return _build_dict_with_elements(elements_with_value) + + # Fallback to an empty dictionary + return _build_dict_with_elements([]) + + +# Builtins inference +register_builtin_transform(infer_bool, "bool") +register_builtin_transform(infer_super, "super") +register_builtin_transform(infer_callable, "callable") +register_builtin_transform(infer_property, "property") +register_builtin_transform(infer_getattr, "getattr") +register_builtin_transform(infer_hasattr, "hasattr") +register_builtin_transform(infer_tuple, "tuple") +register_builtin_transform(infer_set, "set") +register_builtin_transform(infer_list, "list") +register_builtin_transform(infer_dict, "dict") +register_builtin_transform(infer_frozenset, "frozenset") +register_builtin_transform(infer_type, "type") +register_builtin_transform(infer_slice, "slice") +register_builtin_transform(infer_isinstance, "isinstance") +register_builtin_transform(infer_issubclass, "issubclass") +register_builtin_transform(infer_len, "len") +register_builtin_transform(infer_str, "str") +register_builtin_transform(infer_int, "int") +register_builtin_transform(infer_dict_fromkeys, "dict.fromkeys") + + +# Infer object.__new__ calls +MANAGER.register_transform( + nodes.ClassDef, + inference_tip(_infer_object__new__decorator), + _infer_object__new__decorator_check, +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_collections.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_collections.py new file mode 100644 index 0000000..669c6ca --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_collections.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2016, 2018 Claudiu Popa +# Copyright (c) 2016-2017 Łukasz Rogalski +# Copyright (c) 2017 Derek Gustafson +# Copyright (c) 2018 Ioana Tagirta +# Copyright (c) 2019 Hugo van Kemenade + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER +import sys + +import astroid + + +def _collections_transform(): + return astroid.parse( + """ + class defaultdict(dict): + default_factory = None + def __missing__(self, key): pass + def __getitem__(self, key): return default_factory + + """ + + _deque_mock() + + _ordered_dict_mock() + ) + + +def _deque_mock(): + base_deque_class = """ + class deque(object): + maxlen = 0 + def __init__(self, iterable=None, maxlen=None): + self.iterable = iterable or [] + def append(self, x): pass + def appendleft(self, x): pass + def clear(self): pass + def count(self, x): return 0 + def extend(self, iterable): pass + def extendleft(self, iterable): pass + def pop(self): return self.iterable[0] + def popleft(self): return self.iterable[0] + def remove(self, value): pass + def reverse(self): return reversed(self.iterable) + def rotate(self, n=1): return self + def __iter__(self): return self + def __reversed__(self): return self.iterable[::-1] + def __getitem__(self, index): return self.iterable[index] + def __setitem__(self, index, value): pass + def __delitem__(self, index): pass + def __bool__(self): return bool(self.iterable) + def __nonzero__(self): return bool(self.iterable) + def __contains__(self, o): return o in self.iterable + def __len__(self): return len(self.iterable) + def __copy__(self): return deque(self.iterable) + def copy(self): return deque(self.iterable) + def index(self, x, start=0, end=0): return 0 + def insert(self, x, i): pass + def __add__(self, other): pass + def __iadd__(self, other): pass + def __mul__(self, other): pass + def __imul__(self, other): pass + def __rmul__(self, other): pass""" + return base_deque_class + + +def _ordered_dict_mock(): + base_ordered_dict_class = """ + class OrderedDict(dict): + def __reversed__(self): return self[::-1] + def move_to_end(self, key, last=False): pass""" + return base_ordered_dict_class + + +astroid.register_module_extender(astroid.MANAGER, "collections", _collections_transform) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_crypt.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_crypt.py new file mode 100644 index 0000000..491ee23 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_crypt.py @@ -0,0 +1,26 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER +import sys +import astroid + +PY37 = sys.version_info >= (3, 7) + +if PY37: + # Since Python 3.7 Hashing Methods are added + # dynamically to globals() + + def _re_transform(): + return astroid.parse( + """ + from collections import namedtuple + _Method = namedtuple('_Method', 'name ident salt_chars total_size') + + METHOD_SHA512 = _Method('SHA512', '6', 16, 106) + METHOD_SHA256 = _Method('SHA256', '5', 16, 63) + METHOD_BLOWFISH = _Method('BLOWFISH', 2, 'b', 22) + METHOD_MD5 = _Method('MD5', '1', 8, 34) + METHOD_CRYPT = _Method('CRYPT', None, 2, 13) + """ + ) + + astroid.register_module_extender(astroid.MANAGER, "crypt", _re_transform) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_curses.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_curses.py new file mode 100644 index 0000000..68e88b9 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_curses.py @@ -0,0 +1,179 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER +import astroid + + +def _curses_transform(): + return astroid.parse( + """ + A_ALTCHARSET = 1 + A_BLINK = 1 + A_BOLD = 1 + A_DIM = 1 + A_INVIS = 1 + A_ITALIC = 1 + A_NORMAL = 1 + A_PROTECT = 1 + A_REVERSE = 1 + A_STANDOUT = 1 + A_UNDERLINE = 1 + A_HORIZONTAL = 1 + A_LEFT = 1 + A_LOW = 1 + A_RIGHT = 1 + A_TOP = 1 + A_VERTICAL = 1 + A_CHARTEXT = 1 + A_ATTRIBUTES = 1 + A_CHARTEXT = 1 + A_COLOR = 1 + KEY_MIN = 1 + KEY_BREAK = 1 + KEY_DOWN = 1 + KEY_UP = 1 + KEY_LEFT = 1 + KEY_RIGHT = 1 + KEY_HOME = 1 + KEY_BACKSPACE = 1 + KEY_F0 = 1 + KEY_Fn = 1 + KEY_DL = 1 + KEY_IL = 1 + KEY_DC = 1 + KEY_IC = 1 + KEY_EIC = 1 + KEY_CLEAR = 1 + KEY_EOS = 1 + KEY_EOL = 1 + KEY_SF = 1 + KEY_SR = 1 + KEY_NPAGE = 1 + KEY_PPAGE = 1 + KEY_STAB = 1 + KEY_CTAB = 1 + KEY_CATAB = 1 + KEY_ENTER = 1 + KEY_SRESET = 1 + KEY_RESET = 1 + KEY_PRINT = 1 + KEY_LL = 1 + KEY_A1 = 1 + KEY_A3 = 1 + KEY_B2 = 1 + KEY_C1 = 1 + KEY_C3 = 1 + KEY_BTAB = 1 + KEY_BEG = 1 + KEY_CANCEL = 1 + KEY_CLOSE = 1 + KEY_COMMAND = 1 + KEY_COPY = 1 + KEY_CREATE = 1 + KEY_END = 1 + KEY_EXIT = 1 + KEY_FIND = 1 + KEY_HELP = 1 + KEY_MARK = 1 + KEY_MESSAGE = 1 + KEY_MOVE = 1 + KEY_NEXT = 1 + KEY_OPEN = 1 + KEY_OPTIONS = 1 + KEY_PREVIOUS = 1 + KEY_REDO = 1 + KEY_REFERENCE = 1 + KEY_REFRESH = 1 + KEY_REPLACE = 1 + KEY_RESTART = 1 + KEY_RESUME = 1 + KEY_SAVE = 1 + KEY_SBEG = 1 + KEY_SCANCEL = 1 + KEY_SCOMMAND = 1 + KEY_SCOPY = 1 + KEY_SCREATE = 1 + KEY_SDC = 1 + KEY_SDL = 1 + KEY_SELECT = 1 + KEY_SEND = 1 + KEY_SEOL = 1 + KEY_SEXIT = 1 + KEY_SFIND = 1 + KEY_SHELP = 1 + KEY_SHOME = 1 + KEY_SIC = 1 + KEY_SLEFT = 1 + KEY_SMESSAGE = 1 + KEY_SMOVE = 1 + KEY_SNEXT = 1 + KEY_SOPTIONS = 1 + KEY_SPREVIOUS = 1 + KEY_SPRINT = 1 + KEY_SREDO = 1 + KEY_SREPLACE = 1 + KEY_SRIGHT = 1 + KEY_SRSUME = 1 + KEY_SSAVE = 1 + KEY_SSUSPEND = 1 + KEY_SUNDO = 1 + KEY_SUSPEND = 1 + KEY_UNDO = 1 + KEY_MOUSE = 1 + KEY_RESIZE = 1 + KEY_MAX = 1 + ACS_BBSS = 1 + ACS_BLOCK = 1 + ACS_BOARD = 1 + ACS_BSBS = 1 + ACS_BSSB = 1 + ACS_BSSS = 1 + ACS_BTEE = 1 + ACS_BULLET = 1 + ACS_CKBOARD = 1 + ACS_DARROW = 1 + ACS_DEGREE = 1 + ACS_DIAMOND = 1 + ACS_GEQUAL = 1 + ACS_HLINE = 1 + ACS_LANTERN = 1 + ACS_LARROW = 1 + ACS_LEQUAL = 1 + ACS_LLCORNER = 1 + ACS_LRCORNER = 1 + ACS_LTEE = 1 + ACS_NEQUAL = 1 + ACS_PI = 1 + ACS_PLMINUS = 1 + ACS_PLUS = 1 + ACS_RARROW = 1 + ACS_RTEE = 1 + ACS_S1 = 1 + ACS_S3 = 1 + ACS_S7 = 1 + ACS_S9 = 1 + ACS_SBBS = 1 + ACS_SBSB = 1 + ACS_SBSS = 1 + ACS_SSBB = 1 + ACS_SSBS = 1 + ACS_SSSB = 1 + ACS_SSSS = 1 + ACS_STERLING = 1 + ACS_TTEE = 1 + ACS_UARROW = 1 + ACS_ULCORNER = 1 + ACS_URCORNER = 1 + ACS_VLINE = 1 + COLOR_BLACK = 1 + COLOR_BLUE = 1 + COLOR_CYAN = 1 + COLOR_GREEN = 1 + COLOR_MAGENTA = 1 + COLOR_RED = 1 + COLOR_WHITE = 1 + COLOR_YELLOW = 1 + """ + ) + + +astroid.register_module_extender(astroid.MANAGER, "curses", _curses_transform) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_dataclasses.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_dataclasses.py new file mode 100644 index 0000000..7a25e0c --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_dataclasses.py @@ -0,0 +1,50 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER +""" +Astroid hook for the dataclasses library +""" + +import astroid +from astroid import MANAGER + + +DATACLASSES_DECORATORS = frozenset(("dataclasses.dataclass", "dataclass")) + + +def is_decorated_with_dataclass(node, decorator_names=DATACLASSES_DECORATORS): + """Return True if a decorated node has a `dataclass` decorator applied.""" + if not node.decorators: + return False + for decorator_attribute in node.decorators.nodes: + if isinstance(decorator_attribute, astroid.Call): # decorator with arguments + decorator_attribute = decorator_attribute.func + if decorator_attribute.as_string() in decorator_names: + return True + return False + + +def dataclass_transform(node): + """Rewrite a dataclass to be easily understood by pylint""" + + for assign_node in node.body: + if not isinstance(assign_node, (astroid.AnnAssign, astroid.Assign)): + continue + + targets = ( + assign_node.targets + if hasattr(assign_node, "targets") + else [assign_node.target] + ) + for target in targets: + rhs_node = astroid.Unknown( + lineno=assign_node.lineno, + col_offset=assign_node.col_offset, + parent=assign_node, + ) + node.instance_attrs[target.name] = [rhs_node] + node.locals[target.name] = [rhs_node] + + +MANAGER.register_transform( + astroid.ClassDef, dataclass_transform, is_decorated_with_dataclass +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_dateutil.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_dateutil.py new file mode 100644 index 0000000..9fdb9fd --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_dateutil.py @@ -0,0 +1,28 @@ +# Copyright (c) 2015-2016, 2018 Claudiu Popa +# Copyright (c) 2015 raylu +# Copyright (c) 2016 Ceridwen + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""Astroid hooks for dateutil""" + +import textwrap + +from astroid import MANAGER, register_module_extender +from astroid.builder import AstroidBuilder + + +def dateutil_transform(): + return AstroidBuilder(MANAGER).string_build( + textwrap.dedent( + """ + import datetime + def parse(timestr, parserinfo=None, **kwargs): + return datetime.datetime() + """ + ) + ) + + +register_module_extender(MANAGER, "dateutil.parser", dateutil_transform) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_fstrings.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_fstrings.py new file mode 100644 index 0000000..298d58a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_fstrings.py @@ -0,0 +1,51 @@ +# Copyright (c) 2017-2018 Claudiu Popa + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER +import collections +import sys + +import astroid + + +def _clone_node_with_lineno(node, parent, lineno): + cls = node.__class__ + other_fields = node._other_fields + _astroid_fields = node._astroid_fields + init_params = {"lineno": lineno, "col_offset": node.col_offset, "parent": parent} + postinit_params = {param: getattr(node, param) for param in _astroid_fields} + if other_fields: + init_params.update({param: getattr(node, param) for param in other_fields}) + new_node = cls(**init_params) + if hasattr(node, "postinit") and _astroid_fields: + for param, child in postinit_params.items(): + if child and not isinstance(child, collections.Sequence): + cloned_child = _clone_node_with_lineno( + node=child, lineno=new_node.lineno, parent=new_node + ) + postinit_params[param] = cloned_child + new_node.postinit(**postinit_params) + return new_node + + +def _transform_formatted_value(node): + if node.value and node.value.lineno == 1: + if node.lineno != node.value.lineno: + new_node = astroid.FormattedValue( + lineno=node.lineno, col_offset=node.col_offset, parent=node.parent + ) + new_value = _clone_node_with_lineno( + node=node.value, lineno=node.lineno, parent=new_node + ) + new_node.postinit(value=new_value, format_spec=node.format_spec) + return new_node + + +if sys.version_info[:2] >= (3, 6): + # TODO: this fix tries to *patch* http://bugs.python.org/issue29051 + # The problem is that FormattedValue.value, which is a Name node, + # has wrong line numbers, usually 1. This creates problems for pylint, + # which expects correct line numbers for things such as message control. + astroid.MANAGER.register_transform( + astroid.FormattedValue, _transform_formatted_value + ) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_functools.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_functools.py new file mode 100644 index 0000000..d6c6069 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_functools.py @@ -0,0 +1,159 @@ +# Copyright (c) 2016, 2018-2020 Claudiu Popa +# Copyright (c) 2018 hippo91 +# Copyright (c) 2018 Bryce Guinta + +"""Astroid hooks for understanding functools library module.""" +from functools import partial +from itertools import chain + +import astroid +from astroid import arguments +from astroid import BoundMethod +from astroid import extract_node +from astroid import helpers +from astroid.interpreter import objectmodel +from astroid import MANAGER +from astroid import objects + + +LRU_CACHE = "functools.lru_cache" + + +class LruWrappedModel(objectmodel.FunctionModel): + """Special attribute model for functions decorated with functools.lru_cache. + + The said decorators patches at decoration time some functions onto + the decorated function. + """ + + @property + def attr___wrapped__(self): + return self._instance + + @property + def attr_cache_info(self): + cache_info = extract_node( + """ + from functools import _CacheInfo + _CacheInfo(0, 0, 0, 0) + """ + ) + + class CacheInfoBoundMethod(BoundMethod): + def infer_call_result(self, caller, context=None): + yield helpers.safe_infer(cache_info) + + return CacheInfoBoundMethod(proxy=self._instance, bound=self._instance) + + @property + def attr_cache_clear(self): + node = extract_node("""def cache_clear(self): pass""") + return BoundMethod(proxy=node, bound=self._instance.parent.scope()) + + +def _transform_lru_cache(node, context=None): + # TODO: this is not ideal, since the node should be immutable, + # but due to https://github.com/PyCQA/astroid/issues/354, + # there's not much we can do now. + # Replacing the node would work partially, because, + # in pylint, the old node would still be available, leading + # to spurious false positives. + node.special_attributes = LruWrappedModel()(node) + return + + +def _functools_partial_inference(node, context=None): + call = arguments.CallSite.from_call(node, context=context) + number_of_positional = len(call.positional_arguments) + if number_of_positional < 1: + raise astroid.UseInferenceDefault( + "functools.partial takes at least one argument" + ) + if number_of_positional == 1 and not call.keyword_arguments: + raise astroid.UseInferenceDefault( + "functools.partial needs at least to have some filled arguments" + ) + + partial_function = call.positional_arguments[0] + try: + inferred_wrapped_function = next(partial_function.infer(context=context)) + except astroid.InferenceError as exc: + raise astroid.UseInferenceDefault from exc + if inferred_wrapped_function is astroid.Uninferable: + raise astroid.UseInferenceDefault("Cannot infer the wrapped function") + if not isinstance(inferred_wrapped_function, astroid.FunctionDef): + raise astroid.UseInferenceDefault("The wrapped function is not a function") + + # Determine if the passed keywords into the callsite are supported + # by the wrapped function. + function_parameters = chain( + inferred_wrapped_function.args.args or (), + inferred_wrapped_function.args.posonlyargs or (), + inferred_wrapped_function.args.kwonlyargs or (), + ) + parameter_names = set( + param.name + for param in function_parameters + if isinstance(param, astroid.AssignName) + ) + if set(call.keyword_arguments) - parameter_names: + raise astroid.UseInferenceDefault( + "wrapped function received unknown parameters" + ) + + partial_function = objects.PartialFunction( + call, + name=inferred_wrapped_function.name, + doc=inferred_wrapped_function.doc, + lineno=inferred_wrapped_function.lineno, + col_offset=inferred_wrapped_function.col_offset, + parent=inferred_wrapped_function.parent, + ) + partial_function.postinit( + args=inferred_wrapped_function.args, + body=inferred_wrapped_function.body, + decorators=inferred_wrapped_function.decorators, + returns=inferred_wrapped_function.returns, + type_comment_returns=inferred_wrapped_function.type_comment_returns, + type_comment_args=inferred_wrapped_function.type_comment_args, + ) + return iter((partial_function,)) + + +def _looks_like_lru_cache(node): + """Check if the given function node is decorated with lru_cache.""" + if not node.decorators: + return False + for decorator in node.decorators.nodes: + if not isinstance(decorator, astroid.Call): + continue + if _looks_like_functools_member(decorator, "lru_cache"): + return True + return False + + +def _looks_like_functools_member(node, member): + """Check if the given Call node is a functools.partial call""" + if isinstance(node.func, astroid.Name): + return node.func.name == member + elif isinstance(node.func, astroid.Attribute): + return ( + node.func.attrname == member + and isinstance(node.func.expr, astroid.Name) + and node.func.expr.name == "functools" + ) + + +_looks_like_partial = partial(_looks_like_functools_member, member="partial") + + +MANAGER.register_transform( + astroid.FunctionDef, _transform_lru_cache, _looks_like_lru_cache +) + + +MANAGER.register_transform( + astroid.Call, + astroid.inference_tip(_functools_partial_inference), + _looks_like_partial, +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_gi.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_gi.py new file mode 100644 index 0000000..e49f3a2 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_gi.py @@ -0,0 +1,253 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2013-2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2014 Google, Inc. +# Copyright (c) 2014 Cole Robinson +# Copyright (c) 2015-2016, 2018 Claudiu Popa +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2015 David Shea +# Copyright (c) 2016 Jakub Wilk +# Copyright (c) 2016 Giuseppe Scrivano +# Copyright (c) 2018 Christoph Reiter +# Copyright (c) 2019 Philipp Hörist + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""Astroid hooks for the Python 2 GObject introspection bindings. + +Helps with understanding everything imported from 'gi.repository' +""" + +import inspect +import itertools +import sys +import re +import warnings + +from astroid import MANAGER, AstroidBuildingError, nodes +from astroid.builder import AstroidBuilder + + +_inspected_modules = {} + +_identifier_re = r"^[A-Za-z_]\w*$" + +_special_methods = frozenset( + { + "__lt__", + "__le__", + "__eq__", + "__ne__", + "__ge__", + "__gt__", + "__iter__", + "__getitem__", + "__setitem__", + "__delitem__", + "__len__", + "__bool__", + "__nonzero__", + "__next__", + "__str__", + "__len__", + "__contains__", + "__enter__", + "__exit__", + "__repr__", + "__getattr__", + "__setattr__", + "__delattr__", + "__del__", + "__hash__", + } +) + + +def _gi_build_stub(parent): + """ + Inspect the passed module recursively and build stubs for functions, + classes, etc. + """ + classes = {} + functions = {} + constants = {} + methods = {} + for name in dir(parent): + if name.startswith("__") and name not in _special_methods: + continue + + # Check if this is a valid name in python + if not re.match(_identifier_re, name): + continue + + try: + obj = getattr(parent, name) + except: + continue + + if inspect.isclass(obj): + classes[name] = obj + elif inspect.isfunction(obj) or inspect.isbuiltin(obj): + functions[name] = obj + elif inspect.ismethod(obj) or inspect.ismethoddescriptor(obj): + methods[name] = obj + elif ( + str(obj).startswith(", ) + # Only accept function calls with two constant arguments + if len(node.args) != 2: + return False + + if not all(isinstance(arg, nodes.Const) for arg in node.args): + return False + + func = node.func + if isinstance(func, nodes.Attribute): + if func.attrname != "require_version": + return False + if isinstance(func.expr, nodes.Name) and func.expr.name == "gi": + return True + + return False + + if isinstance(func, nodes.Name): + return func.name == "require_version" + + return False + + +def _register_require_version(node): + # Load the gi.require_version locally + try: + import gi + + gi.require_version(node.args[0].value, node.args[1].value) + except Exception: + pass + + return node + + +MANAGER.register_failed_import_hook(_import_gi_module) +MANAGER.register_transform( + nodes.Call, _register_require_version, _looks_like_require_version +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_hashlib.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_hashlib.py new file mode 100644 index 0000000..eb34e15 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_hashlib.py @@ -0,0 +1,69 @@ +# Copyright (c) 2016, 2018 Claudiu Popa +# Copyright (c) 2018 David Poirier +# Copyright (c) 2018 wgehalo +# Copyright (c) 2018 Ioana Tagirta + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER +import sys + +import six + +import astroid + +PY36 = sys.version_info >= (3, 6) + + +def _hashlib_transform(): + signature = "value=''" + template = """ + class %(name)s(object): + def __init__(self, %(signature)s): pass + def digest(self): + return %(digest)s + def copy(self): + return self + def update(self, value): pass + def hexdigest(self): + return '' + @property + def name(self): + return %(name)r + @property + def block_size(self): + return 1 + @property + def digest_size(self): + return 1 + """ + algorithms_with_signature = dict.fromkeys( + ["md5", "sha1", "sha224", "sha256", "sha384", "sha512"], signature + ) + if PY36: + blake2b_signature = "data=b'', *, digest_size=64, key=b'', salt=b'', \ + person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, \ + node_depth=0, inner_size=0, last_node=False" + blake2s_signature = "data=b'', *, digest_size=32, key=b'', salt=b'', \ + person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, \ + node_depth=0, inner_size=0, last_node=False" + new_algorithms = dict.fromkeys( + ["sha3_224", "sha3_256", "sha3_384", "sha3_512", "shake_128", "shake_256"], + signature, + ) + algorithms_with_signature.update(new_algorithms) + algorithms_with_signature.update( + {"blake2b": blake2b_signature, "blake2s": blake2s_signature} + ) + classes = "".join( + template + % { + "name": hashfunc, + "digest": 'b""' if six.PY3 else '""', + "signature": signature, + } + for hashfunc, signature in algorithms_with_signature.items() + ) + return astroid.parse(classes) + + +astroid.register_module_extender(astroid.MANAGER, "hashlib", _hashlib_transform) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_http.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_http.py new file mode 100644 index 0000000..b16464e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_http.py @@ -0,0 +1,211 @@ +# Copyright (c) 2018-2019 Claudiu Popa + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""Astroid brain hints for some of the `http` module.""" +import textwrap + +import astroid +from astroid.builder import AstroidBuilder + + +def _http_transform(): + code = textwrap.dedent( + """ + from collections import namedtuple + _HTTPStatus = namedtuple('_HTTPStatus', 'value phrase description') + + class HTTPStatus: + + @property + def phrase(self): + return "" + @property + def value(self): + return 0 + @property + def description(self): + return "" + + # informational + CONTINUE = _HTTPStatus(100, 'Continue', 'Request received, please continue') + SWITCHING_PROTOCOLS = _HTTPStatus(101, 'Switching Protocols', + 'Switching to new protocol; obey Upgrade header') + PROCESSING = _HTTPStatus(102, 'Processing', '') + OK = _HTTPStatus(200, 'OK', 'Request fulfilled, document follows') + CREATED = _HTTPStatus(201, 'Created', 'Document created, URL follows') + ACCEPTED = _HTTPStatus(202, 'Accepted', + 'Request accepted, processing continues off-line') + NON_AUTHORITATIVE_INFORMATION = _HTTPStatus(203, + 'Non-Authoritative Information', 'Request fulfilled from cache') + NO_CONTENT = _HTTPStatus(204, 'No Content', 'Request fulfilled, nothing follows') + RESET_CONTENT =_HTTPStatus(205, 'Reset Content', 'Clear input form for further input') + PARTIAL_CONTENT = _HTTPStatus(206, 'Partial Content', 'Partial content follows') + MULTI_STATUS = _HTTPStatus(207, 'Multi-Status', '') + ALREADY_REPORTED = _HTTPStatus(208, 'Already Reported', '') + IM_USED = _HTTPStatus(226, 'IM Used', '') + MULTIPLE_CHOICES = _HTTPStatus(300, 'Multiple Choices', + 'Object has several resources -- see URI list') + MOVED_PERMANENTLY = _HTTPStatus(301, 'Moved Permanently', + 'Object moved permanently -- see URI list') + FOUND = _HTTPStatus(302, 'Found', 'Object moved temporarily -- see URI list') + SEE_OTHER = _HTTPStatus(303, 'See Other', 'Object moved -- see Method and URL list') + NOT_MODIFIED = _HTTPStatus(304, 'Not Modified', + 'Document has not changed since given time') + USE_PROXY = _HTTPStatus(305, 'Use Proxy', + 'You must use proxy specified in Location to access this resource') + TEMPORARY_REDIRECT = _HTTPStatus(307, 'Temporary Redirect', + 'Object moved temporarily -- see URI list') + PERMANENT_REDIRECT = _HTTPStatus(308, 'Permanent Redirect', + 'Object moved permanently -- see URI list') + BAD_REQUEST = _HTTPStatus(400, 'Bad Request', + 'Bad request syntax or unsupported method') + UNAUTHORIZED = _HTTPStatus(401, 'Unauthorized', + 'No permission -- see authorization schemes') + PAYMENT_REQUIRED = _HTTPStatus(402, 'Payment Required', + 'No payment -- see charging schemes') + FORBIDDEN = _HTTPStatus(403, 'Forbidden', + 'Request forbidden -- authorization will not help') + NOT_FOUND = _HTTPStatus(404, 'Not Found', + 'Nothing matches the given URI') + METHOD_NOT_ALLOWED = _HTTPStatus(405, 'Method Not Allowed', + 'Specified method is invalid for this resource') + NOT_ACCEPTABLE = _HTTPStatus(406, 'Not Acceptable', + 'URI not available in preferred format') + PROXY_AUTHENTICATION_REQUIRED = _HTTPStatus(407, + 'Proxy Authentication Required', + 'You must authenticate with this proxy before proceeding') + REQUEST_TIMEOUT = _HTTPStatus(408, 'Request Timeout', + 'Request timed out; try again later') + CONFLICT = _HTTPStatus(409, 'Conflict', 'Request conflict') + GONE = _HTTPStatus(410, 'Gone', + 'URI no longer exists and has been permanently removed') + LENGTH_REQUIRED = _HTTPStatus(411, 'Length Required', + 'Client must specify Content-Length') + PRECONDITION_FAILED = _HTTPStatus(412, 'Precondition Failed', + 'Precondition in headers is false') + REQUEST_ENTITY_TOO_LARGE = _HTTPStatus(413, 'Request Entity Too Large', + 'Entity is too large') + REQUEST_URI_TOO_LONG = _HTTPStatus(414, 'Request-URI Too Long', + 'URI is too long') + UNSUPPORTED_MEDIA_TYPE = _HTTPStatus(415, 'Unsupported Media Type', + 'Entity body in unsupported format') + REQUESTED_RANGE_NOT_SATISFIABLE = _HTTPStatus(416, + 'Requested Range Not Satisfiable', + 'Cannot satisfy request range') + EXPECTATION_FAILED = _HTTPStatus(417, 'Expectation Failed', + 'Expect condition could not be satisfied') + MISDIRECTED_REQUEST = _HTTPStatus(421, 'Misdirected Request', + 'Server is not able to produce a response') + UNPROCESSABLE_ENTITY = _HTTPStatus(422, 'Unprocessable Entity') + LOCKED = _HTTPStatus(423, 'Locked') + FAILED_DEPENDENCY = _HTTPStatus(424, 'Failed Dependency') + UPGRADE_REQUIRED = _HTTPStatus(426, 'Upgrade Required') + PRECONDITION_REQUIRED = _HTTPStatus(428, 'Precondition Required', + 'The origin server requires the request to be conditional') + TOO_MANY_REQUESTS = _HTTPStatus(429, 'Too Many Requests', + 'The user has sent too many requests in ' + 'a given amount of time ("rate limiting")') + REQUEST_HEADER_FIELDS_TOO_LARGE = _HTTPStatus(431, + 'Request Header Fields Too Large', + 'The server is unwilling to process the request because its header ' + 'fields are too large') + UNAVAILABLE_FOR_LEGAL_REASONS = _HTTPStatus(451, + 'Unavailable For Legal Reasons', + 'The server is denying access to the ' + 'resource as a consequence of a legal demand') + INTERNAL_SERVER_ERROR = _HTTPStatus(500, 'Internal Server Error', + 'Server got itself in trouble') + NOT_IMPLEMENTED = _HTTPStatus(501, 'Not Implemented', + 'Server does not support this operation') + BAD_GATEWAY = _HTTPStatus(502, 'Bad Gateway', + 'Invalid responses from another server/proxy') + SERVICE_UNAVAILABLE = _HTTPStatus(503, 'Service Unavailable', + 'The server cannot process the request due to a high load') + GATEWAY_TIMEOUT = _HTTPStatus(504, 'Gateway Timeout', + 'The gateway server did not receive a timely response') + HTTP_VERSION_NOT_SUPPORTED = _HTTPStatus(505, 'HTTP Version Not Supported', + 'Cannot fulfill request') + VARIANT_ALSO_NEGOTIATES = _HTTPStatus(506, 'Variant Also Negotiates') + INSUFFICIENT_STORAGE = _HTTPStatus(507, 'Insufficient Storage') + LOOP_DETECTED = _HTTPStatus(508, 'Loop Detected') + NOT_EXTENDED = _HTTPStatus(510, 'Not Extended') + NETWORK_AUTHENTICATION_REQUIRED = _HTTPStatus(511, + 'Network Authentication Required', + 'The client needs to authenticate to gain network access') + """ + ) + return AstroidBuilder(astroid.MANAGER).string_build(code) + + +def _http_client_transform(): + return AstroidBuilder(astroid.MANAGER).string_build( + textwrap.dedent( + """ + from http import HTTPStatus + + CONTINUE = HTTPStatus.CONTINUE + SWITCHING_PROTOCOLS = HTTPStatus.SWITCHING_PROTOCOLS + PROCESSING = HTTPStatus.PROCESSING + OK = HTTPStatus.OK + CREATED = HTTPStatus.CREATED + ACCEPTED = HTTPStatus.ACCEPTED + NON_AUTHORITATIVE_INFORMATION = HTTPStatus.NON_AUTHORITATIVE_INFORMATION + NO_CONTENT = HTTPStatus.NO_CONTENT + RESET_CONTENT = HTTPStatus.RESET_CONTENT + PARTIAL_CONTENT = HTTPStatus.PARTIAL_CONTENT + MULTI_STATUS = HTTPStatus.MULTI_STATUS + ALREADY_REPORTED = HTTPStatus.ALREADY_REPORTED + IM_USED = HTTPStatus.IM_USED + MULTIPLE_CHOICES = HTTPStatus.MULTIPLE_CHOICES + MOVED_PERMANENTLY = HTTPStatus.MOVED_PERMANENTLY + FOUND = HTTPStatus.FOUND + SEE_OTHER = HTTPStatus.SEE_OTHER + NOT_MODIFIED = HTTPStatus.NOT_MODIFIED + USE_PROXY = HTTPStatus.USE_PROXY + TEMPORARY_REDIRECT = HTTPStatus.TEMPORARY_REDIRECT + PERMANENT_REDIRECT = HTTPStatus.PERMANENT_REDIRECT + BAD_REQUEST = HTTPStatus.BAD_REQUEST + UNAUTHORIZED = HTTPStatus.UNAUTHORIZED + PAYMENT_REQUIRED = HTTPStatus.PAYMENT_REQUIRED + FORBIDDEN = HTTPStatus.FORBIDDEN + NOT_FOUND = HTTPStatus.NOT_FOUND + METHOD_NOT_ALLOWED = HTTPStatus.METHOD_NOT_ALLOWED + NOT_ACCEPTABLE = HTTPStatus.NOT_ACCEPTABLE + PROXY_AUTHENTICATION_REQUIRED = HTTPStatus.PROXY_AUTHENTICATION_REQUIRED + REQUEST_TIMEOUT = HTTPStatus.REQUEST_TIMEOUT + CONFLICT = HTTPStatus.CONFLICT + GONE = HTTPStatus.GONE + LENGTH_REQUIRED = HTTPStatus.LENGTH_REQUIRED + PRECONDITION_FAILED = HTTPStatus.PRECONDITION_FAILED + REQUEST_ENTITY_TOO_LARGE = HTTPStatus.REQUEST_ENTITY_TOO_LARGE + REQUEST_URI_TOO_LONG = HTTPStatus.REQUEST_URI_TOO_LONG + UNSUPPORTED_MEDIA_TYPE = HTTPStatus.UNSUPPORTED_MEDIA_TYPE + REQUESTED_RANGE_NOT_SATISFIABLE = HTTPStatus.REQUESTED_RANGE_NOT_SATISFIABLE + EXPECTATION_FAILED = HTTPStatus.EXPECTATION_FAILED + UNPROCESSABLE_ENTITY = HTTPStatus.UNPROCESSABLE_ENTITY + LOCKED = HTTPStatus.LOCKED + FAILED_DEPENDENCY = HTTPStatus.FAILED_DEPENDENCY + UPGRADE_REQUIRED = HTTPStatus.UPGRADE_REQUIRED + PRECONDITION_REQUIRED = HTTPStatus.PRECONDITION_REQUIRED + TOO_MANY_REQUESTS = HTTPStatus.TOO_MANY_REQUESTS + REQUEST_HEADER_FIELDS_TOO_LARGE = HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE + INTERNAL_SERVER_ERROR = HTTPStatus.INTERNAL_SERVER_ERROR + NOT_IMPLEMENTED = HTTPStatus.NOT_IMPLEMENTED + BAD_GATEWAY = HTTPStatus.BAD_GATEWAY + SERVICE_UNAVAILABLE = HTTPStatus.SERVICE_UNAVAILABLE + GATEWAY_TIMEOUT = HTTPStatus.GATEWAY_TIMEOUT + HTTP_VERSION_NOT_SUPPORTED = HTTPStatus.HTTP_VERSION_NOT_SUPPORTED + VARIANT_ALSO_NEGOTIATES = HTTPStatus.VARIANT_ALSO_NEGOTIATES + INSUFFICIENT_STORAGE = HTTPStatus.INSUFFICIENT_STORAGE + LOOP_DETECTED = HTTPStatus.LOOP_DETECTED + NOT_EXTENDED = HTTPStatus.NOT_EXTENDED + NETWORK_AUTHENTICATION_REQUIRED = HTTPStatus.NETWORK_AUTHENTICATION_REQUIRED + """ + ) + ) + + +astroid.register_module_extender(astroid.MANAGER, "http", _http_transform) +astroid.register_module_extender(astroid.MANAGER, "http.client", _http_client_transform) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_io.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_io.py new file mode 100644 index 0000000..c745311 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_io.py @@ -0,0 +1,45 @@ +# Copyright (c) 2016, 2018 Claudiu Popa + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""Astroid brain hints for some of the _io C objects.""" + +import astroid + + +BUFFERED = {"BufferedWriter", "BufferedReader"} +TextIOWrapper = "TextIOWrapper" +FileIO = "FileIO" +BufferedWriter = "BufferedWriter" + + +def _generic_io_transform(node, name, cls): + """Transform the given name, by adding the given *class* as a member of the node.""" + + io_module = astroid.MANAGER.ast_from_module_name("_io") + attribute_object = io_module[cls] + instance = attribute_object.instantiate_class() + node.locals[name] = [instance] + + +def _transform_text_io_wrapper(node): + # This is not always correct, since it can vary with the type of the descriptor, + # being stdout, stderr or stdin. But we cannot get access to the name of the + # stream, which is why we are using the BufferedWriter class as a default + # value + return _generic_io_transform(node, name="buffer", cls=BufferedWriter) + + +def _transform_buffered(node): + return _generic_io_transform(node, name="raw", cls=FileIO) + + +astroid.MANAGER.register_transform( + astroid.ClassDef, _transform_buffered, lambda node: node.name in BUFFERED +) +astroid.MANAGER.register_transform( + astroid.ClassDef, + _transform_text_io_wrapper, + lambda node: node.name == TextIOWrapper, +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_mechanize.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_mechanize.py new file mode 100644 index 0000000..ef62c53 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_mechanize.py @@ -0,0 +1,29 @@ +# Copyright (c) 2012-2013 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2014 Google, Inc. +# Copyright (c) 2015-2016, 2018 Claudiu Popa +# Copyright (c) 2016 Ceridwen + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +from astroid import MANAGER, register_module_extender +from astroid.builder import AstroidBuilder + + +def mechanize_transform(): + return AstroidBuilder(MANAGER).string_build( + """ + +class Browser(object): + def open(self, url, data=None, timeout=None): + return None + def open_novisit(self, url, data=None, timeout=None): + return None + def open_local_file(self, filename): + return None + +""" + ) + + +register_module_extender(MANAGER, "mechanize", mechanize_transform) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_multiprocessing.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_multiprocessing.py new file mode 100644 index 0000000..3629b03 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_multiprocessing.py @@ -0,0 +1,107 @@ +# Copyright (c) 2016, 2018 Claudiu Popa +# Copyright (c) 2019 Hugo van Kemenade + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +import sys + +import astroid +from astroid import exceptions + + +def _multiprocessing_transform(): + module = astroid.parse( + """ + from multiprocessing.managers import SyncManager + def Manager(): + return SyncManager() + """ + ) + # Multiprocessing uses a getattr lookup inside contexts, + # in order to get the attributes they need. Since it's extremely + # dynamic, we use this approach to fake it. + node = astroid.parse( + """ + from multiprocessing.context import DefaultContext, BaseContext + default = DefaultContext() + base = BaseContext() + """ + ) + try: + context = next(node["default"].infer()) + base = next(node["base"].infer()) + except exceptions.InferenceError: + return module + + for node in (context, base): + for key, value in node.locals.items(): + if key.startswith("_"): + continue + + value = value[0] + if isinstance(value, astroid.FunctionDef): + # We need to rebound this, since otherwise + # it will have an extra argument (self). + value = astroid.BoundMethod(value, node) + module[key] = value + return module + + +def _multiprocessing_managers_transform(): + return astroid.parse( + """ + import array + import threading + import multiprocessing.pool as pool + + import six + + class Namespace(object): + pass + + class Value(object): + def __init__(self, typecode, value, lock=True): + self._typecode = typecode + self._value = value + def get(self): + return self._value + def set(self, value): + self._value = value + def __repr__(self): + return '%s(%r, %r)'%(type(self).__name__, self._typecode, self._value) + value = property(get, set) + + def Array(typecode, sequence, lock=True): + return array.array(typecode, sequence) + + class SyncManager(object): + Queue = JoinableQueue = six.moves.queue.Queue + Event = threading.Event + RLock = threading.RLock + BoundedSemaphore = threading.BoundedSemaphore + Condition = threading.Condition + Barrier = threading.Barrier + Pool = pool.Pool + list = list + dict = dict + Value = Value + Array = Array + Namespace = Namespace + __enter__ = lambda self: self + __exit__ = lambda *args: args + + def start(self, initializer=None, initargs=None): + pass + def shutdown(self): + pass + """ + ) + + +astroid.register_module_extender( + astroid.MANAGER, "multiprocessing.managers", _multiprocessing_managers_transform +) +astroid.register_module_extender( + astroid.MANAGER, "multiprocessing", _multiprocessing_transform +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_namedtuple_enum.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_namedtuple_enum.py new file mode 100644 index 0000000..13fcf79 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_namedtuple_enum.py @@ -0,0 +1,455 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2012-2015 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2013-2014 Google, Inc. +# Copyright (c) 2014-2020 Claudiu Popa +# Copyright (c) 2014 Eevee (Alex Munroe) +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2015 Dmitry Pribysh +# Copyright (c) 2015 David Shea +# Copyright (c) 2015 Philip Lorenz +# Copyright (c) 2016 Jakub Wilk +# Copyright (c) 2016 Mateusz Bysiek +# Copyright (c) 2017 Hugo +# Copyright (c) 2017 Łukasz Rogalski +# Copyright (c) 2018 Ville Skyttä +# Copyright (c) 2019 Ashley Whetter + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""Astroid hooks for the Python standard library.""" + +import functools +import keyword +from textwrap import dedent + +from astroid import MANAGER, UseInferenceDefault, inference_tip, InferenceError +from astroid import arguments +from astroid import exceptions +from astroid import nodes +from astroid.builder import AstroidBuilder, extract_node +from astroid import util + + +TYPING_NAMEDTUPLE_BASENAMES = {"NamedTuple", "typing.NamedTuple"} +ENUM_BASE_NAMES = { + "Enum", + "IntEnum", + "enum.Enum", + "enum.IntEnum", + "IntFlag", + "enum.IntFlag", +} + + +def _infer_first(node, context): + if node is util.Uninferable: + raise UseInferenceDefault + try: + value = next(node.infer(context=context)) + if value is util.Uninferable: + raise UseInferenceDefault() + else: + return value + except StopIteration: + raise InferenceError() + + +def _find_func_form_arguments(node, context): + def _extract_namedtuple_arg_or_keyword(position, key_name=None): + + if len(args) > position: + return _infer_first(args[position], context) + if key_name and key_name in found_keywords: + return _infer_first(found_keywords[key_name], context) + + args = node.args + keywords = node.keywords + found_keywords = ( + {keyword.arg: keyword.value for keyword in keywords} if keywords else {} + ) + + name = _extract_namedtuple_arg_or_keyword(position=0, key_name="typename") + names = _extract_namedtuple_arg_or_keyword(position=1, key_name="field_names") + if name and names: + return name.value, names + + raise UseInferenceDefault() + + +def infer_func_form(node, base_type, context=None, enum=False): + """Specific inference function for namedtuple or Python 3 enum. """ + # node is a Call node, class name as first argument and generated class + # attributes as second argument + + # namedtuple or enums list of attributes can be a list of strings or a + # whitespace-separate string + try: + name, names = _find_func_form_arguments(node, context) + try: + attributes = names.value.replace(",", " ").split() + except AttributeError: + if not enum: + attributes = [ + _infer_first(const, context).value for const in names.elts + ] + else: + # Enums supports either iterator of (name, value) pairs + # or mappings. + if hasattr(names, "items") and isinstance(names.items, list): + attributes = [ + _infer_first(const[0], context).value + for const in names.items + if isinstance(const[0], nodes.Const) + ] + elif hasattr(names, "elts"): + # Enums can support either ["a", "b", "c"] + # or [("a", 1), ("b", 2), ...], but they can't + # be mixed. + if all(isinstance(const, nodes.Tuple) for const in names.elts): + attributes = [ + _infer_first(const.elts[0], context).value + for const in names.elts + if isinstance(const, nodes.Tuple) + ] + else: + attributes = [ + _infer_first(const, context).value for const in names.elts + ] + else: + raise AttributeError + if not attributes: + raise AttributeError + except (AttributeError, exceptions.InferenceError): + raise UseInferenceDefault() + + attributes = [attr for attr in attributes if " " not in attr] + + # If we can't infer the name of the class, don't crash, up to this point + # we know it is a namedtuple anyway. + name = name or "Uninferable" + # we want to return a Class node instance with proper attributes set + class_node = nodes.ClassDef(name, "docstring") + class_node.parent = node.parent + # set base class=tuple + class_node.bases.append(base_type) + # XXX add __init__(*attributes) method + for attr in attributes: + fake_node = nodes.EmptyNode() + fake_node.parent = class_node + fake_node.attrname = attr + class_node.instance_attrs[attr] = [fake_node] + return class_node, name, attributes + + +def _has_namedtuple_base(node): + """Predicate for class inference tip + + :type node: ClassDef + :rtype: bool + """ + return set(node.basenames) & TYPING_NAMEDTUPLE_BASENAMES + + +def _looks_like(node, name): + func = node.func + if isinstance(func, nodes.Attribute): + return func.attrname == name + if isinstance(func, nodes.Name): + return func.name == name + return False + + +_looks_like_namedtuple = functools.partial(_looks_like, name="namedtuple") +_looks_like_enum = functools.partial(_looks_like, name="Enum") +_looks_like_typing_namedtuple = functools.partial(_looks_like, name="NamedTuple") + + +def infer_named_tuple(node, context=None): + """Specific inference function for namedtuple Call node""" + tuple_base_name = nodes.Name(name="tuple", parent=node.root()) + class_node, name, attributes = infer_func_form( + node, tuple_base_name, context=context + ) + call_site = arguments.CallSite.from_call(node, context=context) + func = next(extract_node("import collections; collections.namedtuple").infer()) + try: + rename = next(call_site.infer_argument(func, "rename", context)).bool_value() + except InferenceError: + rename = False + + if rename: + attributes = _get_renamed_namedtuple_attributes(attributes) + + replace_args = ", ".join("{arg}=None".format(arg=arg) for arg in attributes) + field_def = ( + " {name} = property(lambda self: self[{index:d}], " + "doc='Alias for field number {index:d}')" + ) + field_defs = "\n".join( + field_def.format(name=name, index=index) + for index, name in enumerate(attributes) + ) + fake = AstroidBuilder(MANAGER).string_build( + """ +class %(name)s(tuple): + __slots__ = () + _fields = %(fields)r + def _asdict(self): + return self.__dict__ + @classmethod + def _make(cls, iterable, new=tuple.__new__, len=len): + return new(cls, iterable) + def _replace(self, %(replace_args)s): + return self + def __getnewargs__(self): + return tuple(self) +%(field_defs)s + """ + % { + "name": name, + "fields": attributes, + "field_defs": field_defs, + "replace_args": replace_args, + } + ) + class_node.locals["_asdict"] = fake.body[0].locals["_asdict"] + class_node.locals["_make"] = fake.body[0].locals["_make"] + class_node.locals["_replace"] = fake.body[0].locals["_replace"] + class_node.locals["_fields"] = fake.body[0].locals["_fields"] + for attr in attributes: + class_node.locals[attr] = fake.body[0].locals[attr] + # we use UseInferenceDefault, we can't be a generator so return an iterator + return iter([class_node]) + + +def _get_renamed_namedtuple_attributes(field_names): + names = list(field_names) + seen = set() + for i, name in enumerate(field_names): + if ( + not all(c.isalnum() or c == "_" for c in name) + or keyword.iskeyword(name) + or not name + or name[0].isdigit() + or name.startswith("_") + or name in seen + ): + names[i] = "_%d" % i + seen.add(name) + return tuple(names) + + +def infer_enum(node, context=None): + """ Specific inference function for enum Call node. """ + enum_meta = extract_node( + """ + class EnumMeta(object): + 'docstring' + def __call__(self, node): + class EnumAttribute(object): + name = '' + value = 0 + return EnumAttribute() + def __iter__(self): + class EnumAttribute(object): + name = '' + value = 0 + return [EnumAttribute()] + def __reversed__(self): + class EnumAttribute(object): + name = '' + value = 0 + return (EnumAttribute, ) + def __next__(self): + return next(iter(self)) + def __getitem__(self, attr): + class Value(object): + @property + def name(self): + return '' + @property + def value(self): + return attr + + return Value() + __members__ = [''] + """ + ) + class_node = infer_func_form(node, enum_meta, context=context, enum=True)[0] + return iter([class_node.instantiate_class()]) + + +INT_FLAG_ADDITION_METHODS = """ + def __or__(self, other): + return {name}(self.value | other.value) + def __and__(self, other): + return {name}(self.value & other.value) + def __xor__(self, other): + return {name}(self.value ^ other.value) + def __add__(self, other): + return {name}(self.value + other.value) + def __div__(self, other): + return {name}(self.value / other.value) + def __invert__(self): + return {name}(~self.value) + def __mul__(self, other): + return {name}(self.value * other.value) +""" + + +def infer_enum_class(node): + """ Specific inference for enums. """ + for basename in node.basenames: + # TODO: doesn't handle subclasses yet. This implementation + # is a hack to support enums. + if basename not in ENUM_BASE_NAMES: + continue + if node.root().name == "enum": + # Skip if the class is directly from enum module. + break + for local, values in node.locals.items(): + if any(not isinstance(value, nodes.AssignName) for value in values): + continue + + targets = [] + stmt = values[0].statement() + if isinstance(stmt, nodes.Assign): + if isinstance(stmt.targets[0], nodes.Tuple): + targets = stmt.targets[0].itered() + else: + targets = stmt.targets + elif isinstance(stmt, nodes.AnnAssign): + targets = [stmt.target] + else: + continue + + inferred_return_value = None + if isinstance(stmt, nodes.Assign): + if isinstance(stmt.value, nodes.Const): + if isinstance(stmt.value.value, str): + inferred_return_value = repr(stmt.value.value) + else: + inferred_return_value = stmt.value.value + else: + inferred_return_value = stmt.value.as_string() + + new_targets = [] + for target in targets: + # Replace all the assignments with our mocked class. + classdef = dedent( + """ + class {name}({types}): + @property + def value(self): + return {return_value} + @property + def name(self): + return "{name}" + """.format( + name=target.name, + types=", ".join(node.basenames), + return_value=inferred_return_value, + ) + ) + if "IntFlag" in basename: + # Alright, we need to add some additional methods. + # Unfortunately we still can't infer the resulting objects as + # Enum members, but once we'll be able to do that, the following + # should result in some nice symbolic execution + classdef += INT_FLAG_ADDITION_METHODS.format(name=target.name) + + fake = AstroidBuilder(MANAGER).string_build(classdef)[target.name] + fake.parent = target.parent + for method in node.mymethods(): + fake.locals[method.name] = [method] + new_targets.append(fake.instantiate_class()) + node.locals[local] = new_targets + break + return node + + +def infer_typing_namedtuple_class(class_node, context=None): + """Infer a subclass of typing.NamedTuple""" + # Check if it has the corresponding bases + annassigns_fields = [ + annassign.target.name + for annassign in class_node.body + if isinstance(annassign, nodes.AnnAssign) + ] + code = dedent( + """ + from collections import namedtuple + namedtuple({typename!r}, {fields!r}) + """ + ).format(typename=class_node.name, fields=",".join(annassigns_fields)) + node = extract_node(code) + generated_class_node = next(infer_named_tuple(node, context)) + for method in class_node.mymethods(): + generated_class_node.locals[method.name] = [method] + + for assign in class_node.body: + if not isinstance(assign, nodes.Assign): + continue + + for target in assign.targets: + attr = target.name + generated_class_node.locals[attr] = class_node.locals[attr] + + return iter((generated_class_node,)) + + +def infer_typing_namedtuple(node, context=None): + """Infer a typing.NamedTuple(...) call.""" + # This is essentially a namedtuple with different arguments + # so we extract the args and infer a named tuple. + try: + func = next(node.func.infer()) + except InferenceError: + raise UseInferenceDefault + + if func.qname() != "typing.NamedTuple": + raise UseInferenceDefault + + if len(node.args) != 2: + raise UseInferenceDefault + + if not isinstance(node.args[1], (nodes.List, nodes.Tuple)): + raise UseInferenceDefault + + names = [] + for elt in node.args[1].elts: + if not isinstance(elt, (nodes.List, nodes.Tuple)): + raise UseInferenceDefault + if len(elt.elts) != 2: + raise UseInferenceDefault + names.append(elt.elts[0].as_string()) + + typename = node.args[0].as_string() + if names: + field_names = "({},)".format(",".join(names)) + else: + field_names = "''" + node = extract_node( + "namedtuple({typename}, {fields})".format(typename=typename, fields=field_names) + ) + return infer_named_tuple(node, context) + + +MANAGER.register_transform( + nodes.Call, inference_tip(infer_named_tuple), _looks_like_namedtuple +) +MANAGER.register_transform(nodes.Call, inference_tip(infer_enum), _looks_like_enum) +MANAGER.register_transform( + nodes.ClassDef, + infer_enum_class, + predicate=lambda cls: any( + basename for basename in cls.basenames if basename in ENUM_BASE_NAMES + ), +) +MANAGER.register_transform( + nodes.ClassDef, inference_tip(infer_typing_namedtuple_class), _has_namedtuple_base +) +MANAGER.register_transform( + nodes.Call, inference_tip(infer_typing_namedtuple), _looks_like_typing_namedtuple +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_nose.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_nose.py new file mode 100644 index 0000000..d0280a3 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_nose.py @@ -0,0 +1,77 @@ +# Copyright (c) 2015-2016, 2018 Claudiu Popa +# Copyright (c) 2016 Ceridwen + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + + +"""Hooks for nose library.""" + +import re +import textwrap + +import astroid +import astroid.builder + +_BUILDER = astroid.builder.AstroidBuilder(astroid.MANAGER) + + +def _pep8(name, caps=re.compile("([A-Z])")): + return caps.sub(lambda m: "_" + m.groups()[0].lower(), name) + + +def _nose_tools_functions(): + """Get an iterator of names and bound methods.""" + module = _BUILDER.string_build( + textwrap.dedent( + """ + import unittest + + class Test(unittest.TestCase): + pass + a = Test() + """ + ) + ) + try: + case = next(module["a"].infer()) + except astroid.InferenceError: + return + for method in case.methods(): + if method.name.startswith("assert") and "_" not in method.name: + pep8_name = _pep8(method.name) + yield pep8_name, astroid.BoundMethod(method, case) + if method.name == "assertEqual": + # nose also exports assert_equals. + yield "assert_equals", astroid.BoundMethod(method, case) + + +def _nose_tools_transform(node): + for method_name, method in _nose_tools_functions(): + node.locals[method_name] = [method] + + +def _nose_tools_trivial_transform(): + """Custom transform for the nose.tools module.""" + stub = _BUILDER.string_build("""__all__ = []""") + all_entries = ["ok_", "eq_"] + + for pep8_name, method in _nose_tools_functions(): + all_entries.append(pep8_name) + stub[pep8_name] = method + + # Update the __all__ variable, since nose.tools + # does this manually with .append. + all_assign = stub["__all__"].parent + all_object = astroid.List(all_entries) + all_object.parent = all_assign + all_assign.value = all_object + return stub + + +astroid.register_module_extender( + astroid.MANAGER, "nose.tools.trivial", _nose_tools_trivial_transform +) +astroid.MANAGER.register_transform( + astroid.Module, _nose_tools_transform, lambda n: n.name == "nose.tools" +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py new file mode 100644 index 0000000..62dfe99 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py @@ -0,0 +1,23 @@ +# Copyright (c) 2019 hippo91 + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + + +"""Astroid hooks for numpy.core.fromnumeric module.""" + +import astroid + + +def numpy_core_fromnumeric_transform(): + return astroid.parse( + """ + def sum(a, axis=None, dtype=None, out=None, keepdims=None, initial=None): + return numpy.ndarray([0, 0]) + """ + ) + + +astroid.register_module_extender( + astroid.MANAGER, "numpy.core.fromnumeric", numpy_core_fromnumeric_transform +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_function_base.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_function_base.py new file mode 100644 index 0000000..58aa0a9 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_function_base.py @@ -0,0 +1,29 @@ +# Copyright (c) 2019 hippo91 + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + + +"""Astroid hooks for numpy.core.function_base module.""" + +import functools +import astroid +from brain_numpy_utils import looks_like_numpy_member, infer_numpy_member + + +METHODS_TO_BE_INFERRED = { + "linspace": """def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0): + return numpy.ndarray([0, 0])""", + "logspace": """def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None, axis=0): + return numpy.ndarray([0, 0])""", + "geomspace": """def geomspace(start, stop, num=50, endpoint=True, dtype=None, axis=0): + return numpy.ndarray([0, 0])""", +} + +for func_name, func_src in METHODS_TO_BE_INFERRED.items(): + inference_function = functools.partial(infer_numpy_member, func_src) + astroid.MANAGER.register_transform( + astroid.Attribute, + astroid.inference_tip(inference_function), + functools.partial(looks_like_numpy_member, func_name), + ) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_multiarray.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_multiarray.py new file mode 100644 index 0000000..b2e32bc --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_multiarray.py @@ -0,0 +1,92 @@ +# Copyright (c) 2019-2020 hippo91 + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + + +"""Astroid hooks for numpy.core.multiarray module.""" + +import functools +import astroid +from brain_numpy_utils import looks_like_numpy_member, infer_numpy_member + + +def numpy_core_multiarray_transform(): + return astroid.parse( + """ + # different functions defined in multiarray.py + def inner(a, b): + return numpy.ndarray([0, 0]) + + def vdot(a, b): + return numpy.ndarray([0, 0]) + """ + ) + + +astroid.register_module_extender( + astroid.MANAGER, "numpy.core.multiarray", numpy_core_multiarray_transform +) + + +METHODS_TO_BE_INFERRED = { + "array": """def array(object, dtype=None, copy=True, order='K', subok=False, ndmin=0): + return numpy.ndarray([0, 0])""", + "dot": """def dot(a, b, out=None): + return numpy.ndarray([0, 0])""", + "empty_like": """def empty_like(a, dtype=None, order='K', subok=True): + return numpy.ndarray((0, 0))""", + "concatenate": """def concatenate(arrays, axis=None, out=None): + return numpy.ndarray((0, 0))""", + "where": """def where(condition, x=None, y=None): + return numpy.ndarray([0, 0])""", + "empty": """def empty(shape, dtype=float, order='C'): + return numpy.ndarray([0, 0])""", + "bincount": """def bincount(x, weights=None, minlength=0): + return numpy.ndarray([0, 0])""", + "busday_count": """def busday_count(begindates, enddates, weekmask='1111100', holidays=[], busdaycal=None, out=None): + return numpy.ndarray([0, 0])""", + "busday_offset": """def busday_offset(dates, offsets, roll='raise', weekmask='1111100', holidays=None, busdaycal=None, out=None): + return numpy.ndarray([0, 0])""", + "can_cast": """def can_cast(from_, to, casting='safe'): + return True""", + "copyto": """def copyto(dst, src, casting='same_kind', where=True): + return None""", + "datetime_as_string": """def datetime_as_string(arr, unit=None, timezone='naive', casting='same_kind'): + return numpy.ndarray([0, 0])""", + "is_busday": """def is_busday(dates, weekmask='1111100', holidays=None, busdaycal=None, out=None): + return numpy.ndarray([0, 0])""", + "lexsort": """def lexsort(keys, axis=-1): + return numpy.ndarray([0, 0])""", + "may_share_memory": """def may_share_memory(a, b, max_work=None): + return True""", + # Not yet available because dtype is not yet present in those brains + # "min_scalar_type": """def min_scalar_type(a): + # return numpy.dtype('int16')""", + "packbits": """def packbits(a, axis=None, bitorder='big'): + return numpy.ndarray([0, 0])""", + # Not yet available because dtype is not yet present in those brains + # "result_type": """def result_type(*arrays_and_dtypes): + # return numpy.dtype('int16')""", + "shares_memory": """def shares_memory(a, b, max_work=None): + return True""", + "unpackbits": """def unpackbits(a, axis=None, count=None, bitorder='big'): + return numpy.ndarray([0, 0])""", + "unravel_index": """def unravel_index(indices, shape, order='C'): + return (numpy.ndarray([0, 0]),)""", + "zeros": """def zeros(shape, dtype=float, order='C'): + return numpy.ndarray([0, 0])""", +} + +for method_name, function_src in METHODS_TO_BE_INFERRED.items(): + inference_function = functools.partial(infer_numpy_member, function_src) + astroid.MANAGER.register_transform( + astroid.Attribute, + astroid.inference_tip(inference_function), + functools.partial(looks_like_numpy_member, method_name), + ) + astroid.MANAGER.register_transform( + astroid.Name, + astroid.inference_tip(inference_function), + functools.partial(looks_like_numpy_member, method_name), + ) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_numeric.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_numeric.py new file mode 100644 index 0000000..2a6f37e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_numeric.py @@ -0,0 +1,43 @@ +# Copyright (c) 2019 hippo91 + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + + +"""Astroid hooks for numpy.core.numeric module.""" + +import functools +import astroid +from brain_numpy_utils import looks_like_numpy_member, infer_numpy_member + + +def numpy_core_numeric_transform(): + return astroid.parse( + """ + # different functions defined in numeric.py + import numpy + def zeros_like(a, dtype=None, order='K', subok=True): return numpy.ndarray((0, 0)) + def ones_like(a, dtype=None, order='K', subok=True): return numpy.ndarray((0, 0)) + def full_like(a, fill_value, dtype=None, order='K', subok=True): return numpy.ndarray((0, 0)) + """ + ) + + +astroid.register_module_extender( + astroid.MANAGER, "numpy.core.numeric", numpy_core_numeric_transform +) + + +METHODS_TO_BE_INFERRED = { + "ones": """def ones(shape, dtype=None, order='C'): + return numpy.ndarray([0, 0])""" +} + + +for method_name, function_src in METHODS_TO_BE_INFERRED.items(): + inference_function = functools.partial(infer_numpy_member, function_src) + astroid.MANAGER.register_transform( + astroid.Attribute, + astroid.inference_tip(inference_function), + functools.partial(looks_like_numpy_member, method_name), + ) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_numerictypes.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_numerictypes.py new file mode 100644 index 0000000..6ac4a14 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_numerictypes.py @@ -0,0 +1,254 @@ +# Copyright (c) 2019-2020 hippo91 + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +# TODO(hippo91) : correct the methods signature. + +"""Astroid hooks for numpy.core.numerictypes module.""" + +import astroid + + +def numpy_core_numerictypes_transform(): + # TODO: Uniformize the generic API with the ndarray one. + # According to numpy doc the generic object should expose + # the same API than ndarray. This has been done here partially + # through the astype method. + return astroid.parse( + """ + # different types defined in numerictypes.py + class generic(object): + def __init__(self, value): + self.T = None + self.base = None + self.data = None + self.dtype = None + self.flags = None + self.flat = None + self.imag = None + self.itemsize = None + self.nbytes = None + self.ndim = None + self.real = None + self.size = None + self.strides = None + + def all(self): return uninferable + def any(self): return uninferable + def argmax(self): return uninferable + def argmin(self): return uninferable + def argsort(self): return uninferable + def astype(self, dtype, order='K', casting='unsafe', subok=True, copy=True): return np.ndarray([0, 0]) + def base(self): return uninferable + def byteswap(self): return uninferable + def choose(self): return uninferable + def clip(self): return uninferable + def compress(self): return uninferable + def conj(self): return uninferable + def conjugate(self): return uninferable + def copy(self): return uninferable + def cumprod(self): return uninferable + def cumsum(self): return uninferable + def data(self): return uninferable + def diagonal(self): return uninferable + def dtype(self): return uninferable + def dump(self): return uninferable + def dumps(self): return uninferable + def fill(self): return uninferable + def flags(self): return uninferable + def flat(self): return uninferable + def flatten(self): return uninferable + def getfield(self): return uninferable + def imag(self): return uninferable + def item(self): return uninferable + def itemset(self): return uninferable + def itemsize(self): return uninferable + def max(self): return uninferable + def mean(self): return uninferable + def min(self): return uninferable + def nbytes(self): return uninferable + def ndim(self): return uninferable + def newbyteorder(self): return uninferable + def nonzero(self): return uninferable + def prod(self): return uninferable + def ptp(self): return uninferable + def put(self): return uninferable + def ravel(self): return uninferable + def real(self): return uninferable + def repeat(self): return uninferable + def reshape(self): return uninferable + def resize(self): return uninferable + def round(self): return uninferable + def searchsorted(self): return uninferable + def setfield(self): return uninferable + def setflags(self): return uninferable + def shape(self): return uninferable + def size(self): return uninferable + def sort(self): return uninferable + def squeeze(self): return uninferable + def std(self): return uninferable + def strides(self): return uninferable + def sum(self): return uninferable + def swapaxes(self): return uninferable + def take(self): return uninferable + def tobytes(self): return uninferable + def tofile(self): return uninferable + def tolist(self): return uninferable + def tostring(self): return uninferable + def trace(self): return uninferable + def transpose(self): return uninferable + def var(self): return uninferable + def view(self): return uninferable + + + class dtype(object): + def __init__(self, obj, align=False, copy=False): + self.alignment = None + self.base = None + self.byteorder = None + self.char = None + self.descr = None + self.fields = None + self.flags = None + self.hasobject = None + self.isalignedstruct = None + self.isbuiltin = None + self.isnative = None + self.itemsize = None + self.kind = None + self.metadata = None + self.name = None + self.names = None + self.num = None + self.shape = None + self.str = None + self.subdtype = None + self.type = None + + def newbyteorder(self, new_order='S'): return uninferable + def __neg__(self): return uninferable + + class busdaycalendar(object): + def __init__(self, weekmask='1111100', holidays=None): + self.holidays = None + self.weekmask = None + + class flexible(generic): pass + class bool_(generic): pass + class number(generic): + def __neg__(self): return uninferable + class datetime64(generic): + def __init__(self, nb, unit=None): pass + + + class void(flexible): + def __init__(self, *args, **kwargs): + self.base = None + self.dtype = None + self.flags = None + def getfield(self): return uninferable + def setfield(self): return uninferable + + + class character(flexible): pass + + + class integer(number): + def __init__(self, value): + self.denominator = None + self.numerator = None + + + class inexact(number): pass + + + class str_(str, character): + def maketrans(self, x, y=None, z=None): return uninferable + + + class bytes_(bytes, character): + def fromhex(self, string): return uninferable + def maketrans(self, frm, to): return uninferable + + + class signedinteger(integer): pass + + + class unsignedinteger(integer): pass + + + class complexfloating(inexact): pass + + + class floating(inexact): pass + + + class float64(floating, float): + def fromhex(self, string): return uninferable + + + class uint64(unsignedinteger): pass + class complex64(complexfloating): pass + class int16(signedinteger): pass + class float96(floating): pass + class int8(signedinteger): pass + class uint32(unsignedinteger): pass + class uint8(unsignedinteger): pass + class _typedict(dict): pass + class complex192(complexfloating): pass + class timedelta64(signedinteger): + def __init__(self, nb, unit=None): pass + class int32(signedinteger): pass + class uint16(unsignedinteger): pass + class float32(floating): pass + class complex128(complexfloating, complex): pass + class float16(floating): pass + class int64(signedinteger): pass + + buffer_type = memoryview + bool8 = bool_ + byte = int8 + bytes0 = bytes_ + cdouble = complex128 + cfloat = complex128 + clongdouble = complex192 + clongfloat = complex192 + complex_ = complex128 + csingle = complex64 + double = float64 + float_ = float64 + half = float16 + int0 = int32 + int_ = int32 + intc = int32 + intp = int32 + long = int32 + longcomplex = complex192 + longdouble = float96 + longfloat = float96 + longlong = int64 + object0 = object_ + object_ = object_ + short = int16 + single = float32 + singlecomplex = complex64 + str0 = str_ + string_ = bytes_ + ubyte = uint8 + uint = uint32 + uint0 = uint32 + uintc = uint32 + uintp = uint32 + ulonglong = uint64 + unicode = str_ + unicode_ = str_ + ushort = uint16 + void0 = void + """ + ) + + +astroid.register_module_extender( + astroid.MANAGER, "numpy.core.numerictypes", numpy_core_numerictypes_transform +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_umath.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_umath.py new file mode 100644 index 0000000..897961e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_core_umath.py @@ -0,0 +1,147 @@ +# Copyright (c) 2019 hippo91 + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + + +"""Astroid hooks for numpy.core.umath module.""" + +import astroid + + +def numpy_core_umath_transform(): + ufunc_optional_keyword_arguments = ( + """out=None, where=True, casting='same_kind', order='K', """ + """dtype=None, subok=True""" + ) + return astroid.parse( + """ + class FakeUfunc: + def __init__(self): + self.__doc__ = str() + self.__name__ = str() + self.nin = 0 + self.nout = 0 + self.nargs = 0 + self.ntypes = 0 + self.types = None + self.identity = None + self.signature = None + + @classmethod + def reduce(cls, a, axis=None, dtype=None, out=None): + return numpy.ndarray([0, 0]) + + @classmethod + def accumulate(cls, array, axis=None, dtype=None, out=None): + return numpy.ndarray([0, 0]) + + @classmethod + def reduceat(cls, a, indices, axis=None, dtype=None, out=None): + return numpy.ndarray([0, 0]) + + @classmethod + def outer(cls, A, B, **kwargs): + return numpy.ndarray([0, 0]) + + @classmethod + def at(cls, a, indices, b=None): + return numpy.ndarray([0, 0]) + + class FakeUfuncOneArg(FakeUfunc): + def __call__(self, x, {opt_args:s}): + return numpy.ndarray([0, 0]) + + class FakeUfuncOneArgBis(FakeUfunc): + def __call__(self, x, {opt_args:s}): + return numpy.ndarray([0, 0]), numpy.ndarray([0, 0]) + + class FakeUfuncTwoArgs(FakeUfunc): + def __call__(self, x1, x2, {opt_args:s}): + return numpy.ndarray([0, 0]) + + # Constants + e = 2.718281828459045 + euler_gamma = 0.5772156649015329 + + # One arg functions with optional kwargs + arccos = FakeUfuncOneArg() + arccosh = FakeUfuncOneArg() + arcsin = FakeUfuncOneArg() + arcsinh = FakeUfuncOneArg() + arctan = FakeUfuncOneArg() + arctanh = FakeUfuncOneArg() + cbrt = FakeUfuncOneArg() + conj = FakeUfuncOneArg() + conjugate = FakeUfuncOneArg() + cosh = FakeUfuncOneArg() + deg2rad = FakeUfuncOneArg() + exp2 = FakeUfuncOneArg() + expm1 = FakeUfuncOneArg() + fabs = FakeUfuncOneArg() + frexp = FakeUfuncOneArgBis() + isfinite = FakeUfuncOneArg() + isinf = FakeUfuncOneArg() + log = FakeUfuncOneArg() + log1p = FakeUfuncOneArg() + log2 = FakeUfuncOneArg() + logical_not = FakeUfuncOneArg() + modf = FakeUfuncOneArgBis() + negative = FakeUfuncOneArg() + positive = FakeUfuncOneArg() + rad2deg = FakeUfuncOneArg() + reciprocal = FakeUfuncOneArg() + rint = FakeUfuncOneArg() + sign = FakeUfuncOneArg() + signbit = FakeUfuncOneArg() + sinh = FakeUfuncOneArg() + spacing = FakeUfuncOneArg() + square = FakeUfuncOneArg() + tan = FakeUfuncOneArg() + tanh = FakeUfuncOneArg() + trunc = FakeUfuncOneArg() + + # Two args functions with optional kwargs + bitwise_and = FakeUfuncTwoArgs() + bitwise_or = FakeUfuncTwoArgs() + bitwise_xor = FakeUfuncTwoArgs() + copysign = FakeUfuncTwoArgs() + divide = FakeUfuncTwoArgs() + divmod = FakeUfuncTwoArgs() + equal = FakeUfuncTwoArgs() + float_power = FakeUfuncTwoArgs() + floor_divide = FakeUfuncTwoArgs() + fmax = FakeUfuncTwoArgs() + fmin = FakeUfuncTwoArgs() + fmod = FakeUfuncTwoArgs() + greater = FakeUfuncTwoArgs() + gcd = FakeUfuncTwoArgs() + hypot = FakeUfuncTwoArgs() + heaviside = FakeUfuncTwoArgs() + lcm = FakeUfuncTwoArgs() + ldexp = FakeUfuncTwoArgs() + left_shift = FakeUfuncTwoArgs() + less = FakeUfuncTwoArgs() + logaddexp = FakeUfuncTwoArgs() + logaddexp2 = FakeUfuncTwoArgs() + logical_and = FakeUfuncTwoArgs() + logical_or = FakeUfuncTwoArgs() + logical_xor = FakeUfuncTwoArgs() + maximum = FakeUfuncTwoArgs() + minimum = FakeUfuncTwoArgs() + nextafter = FakeUfuncTwoArgs() + not_equal = FakeUfuncTwoArgs() + power = FakeUfuncTwoArgs() + remainder = FakeUfuncTwoArgs() + right_shift = FakeUfuncTwoArgs() + subtract = FakeUfuncTwoArgs() + true_divide = FakeUfuncTwoArgs() + """.format( + opt_args=ufunc_optional_keyword_arguments + ) + ) + + +astroid.register_module_extender( + astroid.MANAGER, "numpy.core.umath", numpy_core_umath_transform +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_ndarray.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_ndarray.py new file mode 100644 index 0000000..d40a7dd --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_ndarray.py @@ -0,0 +1,153 @@ +# Copyright (c) 2015-2016, 2018-2019 Claudiu Popa +# Copyright (c) 2016 Ceridwen +# Copyright (c) 2017-2020 hippo91 + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + + +"""Astroid hooks for numpy ndarray class.""" + +import functools +import astroid + + +def infer_numpy_ndarray(node, context=None): + ndarray = """ + class ndarray(object): + def __init__(self, shape, dtype=float, buffer=None, offset=0, + strides=None, order=None): + self.T = None + self.base = None + self.ctypes = None + self.data = None + self.dtype = None + self.flags = None + self.flat = None + self.imag = np.ndarray([0, 0]) + self.itemsize = None + self.nbytes = None + self.ndim = None + self.real = np.ndarray([0, 0]) + self.shape = numpy.ndarray([0, 0]) + self.size = None + self.strides = None + + def __abs__(self): return numpy.ndarray([0, 0]) + def __add__(self, value): return numpy.ndarray([0, 0]) + def __and__(self, value): return numpy.ndarray([0, 0]) + def __array__(self, dtype=None): return numpy.ndarray([0, 0]) + def __array_wrap__(self, obj): return numpy.ndarray([0, 0]) + def __contains__(self, key): return True + def __copy__(self): return numpy.ndarray([0, 0]) + def __deepcopy__(self, memo): return numpy.ndarray([0, 0]) + def __divmod__(self, value): return (numpy.ndarray([0, 0]), numpy.ndarray([0, 0])) + def __eq__(self, value): return numpy.ndarray([0, 0]) + def __float__(self): return 0. + def __floordiv__(self): return numpy.ndarray([0, 0]) + def __ge__(self, value): return numpy.ndarray([0, 0]) + def __getitem__(self, key): return uninferable + def __gt__(self, value): return numpy.ndarray([0, 0]) + def __iadd__(self, value): return numpy.ndarray([0, 0]) + def __iand__(self, value): return numpy.ndarray([0, 0]) + def __ifloordiv__(self, value): return numpy.ndarray([0, 0]) + def __ilshift__(self, value): return numpy.ndarray([0, 0]) + def __imod__(self, value): return numpy.ndarray([0, 0]) + def __imul__(self, value): return numpy.ndarray([0, 0]) + def __int__(self): return 0 + def __invert__(self): return numpy.ndarray([0, 0]) + def __ior__(self, value): return numpy.ndarray([0, 0]) + def __ipow__(self, value): return numpy.ndarray([0, 0]) + def __irshift__(self, value): return numpy.ndarray([0, 0]) + def __isub__(self, value): return numpy.ndarray([0, 0]) + def __itruediv__(self, value): return numpy.ndarray([0, 0]) + def __ixor__(self, value): return numpy.ndarray([0, 0]) + def __le__(self, value): return numpy.ndarray([0, 0]) + def __len__(self): return 1 + def __lshift__(self, value): return numpy.ndarray([0, 0]) + def __lt__(self, value): return numpy.ndarray([0, 0]) + def __matmul__(self, value): return numpy.ndarray([0, 0]) + def __mod__(self, value): return numpy.ndarray([0, 0]) + def __mul__(self, value): return numpy.ndarray([0, 0]) + def __ne__(self, value): return numpy.ndarray([0, 0]) + def __neg__(self): return numpy.ndarray([0, 0]) + def __or__(self): return numpy.ndarray([0, 0]) + def __pos__(self): return numpy.ndarray([0, 0]) + def __pow__(self): return numpy.ndarray([0, 0]) + def __repr__(self): return str() + def __rshift__(self): return numpy.ndarray([0, 0]) + def __setitem__(self, key, value): return uninferable + def __str__(self): return str() + def __sub__(self, value): return numpy.ndarray([0, 0]) + def __truediv__(self, value): return numpy.ndarray([0, 0]) + def __xor__(self, value): return numpy.ndarray([0, 0]) + def all(self, axis=None, out=None, keepdims=False): return np.ndarray([0, 0]) + def any(self, axis=None, out=None, keepdims=False): return np.ndarray([0, 0]) + def argmax(self, axis=None, out=None): return np.ndarray([0, 0]) + def argmin(self, axis=None, out=None): return np.ndarray([0, 0]) + def argpartition(self, kth, axis=-1, kind='introselect', order=None): return np.ndarray([0, 0]) + def argsort(self, axis=-1, kind='quicksort', order=None): return np.ndarray([0, 0]) + def astype(self, dtype, order='K', casting='unsafe', subok=True, copy=True): return np.ndarray([0, 0]) + def byteswap(self, inplace=False): return np.ndarray([0, 0]) + def choose(self, choices, out=None, mode='raise'): return np.ndarray([0, 0]) + def clip(self, min=None, max=None, out=None): return np.ndarray([0, 0]) + def compress(self, condition, axis=None, out=None): return np.ndarray([0, 0]) + def conj(self): return np.ndarray([0, 0]) + def conjugate(self): return np.ndarray([0, 0]) + def copy(self, order='C'): return np.ndarray([0, 0]) + def cumprod(self, axis=None, dtype=None, out=None): return np.ndarray([0, 0]) + def cumsum(self, axis=None, dtype=None, out=None): return np.ndarray([0, 0]) + def diagonal(self, offset=0, axis1=0, axis2=1): return np.ndarray([0, 0]) + def dot(self, b, out=None): return np.ndarray([0, 0]) + def dump(self, file): return None + def dumps(self): return str() + def fill(self, value): return None + def flatten(self, order='C'): return np.ndarray([0, 0]) + def getfield(self, dtype, offset=0): return np.ndarray([0, 0]) + def item(self, *args): return uninferable + def itemset(self, *args): return None + def max(self, axis=None, out=None): return np.ndarray([0, 0]) + def mean(self, axis=None, dtype=None, out=None, keepdims=False): return np.ndarray([0, 0]) + def min(self, axis=None, out=None, keepdims=False): return np.ndarray([0, 0]) + def newbyteorder(self, new_order='S'): return np.ndarray([0, 0]) + def nonzero(self): return (1,) + def partition(self, kth, axis=-1, kind='introselect', order=None): return None + def prod(self, axis=None, dtype=None, out=None, keepdims=False): return np.ndarray([0, 0]) + def ptp(self, axis=None, out=None): return np.ndarray([0, 0]) + def put(self, indices, values, mode='raise'): return None + def ravel(self, order='C'): return np.ndarray([0, 0]) + def repeat(self, repeats, axis=None): return np.ndarray([0, 0]) + def reshape(self, shape, order='C'): return np.ndarray([0, 0]) + def resize(self, new_shape, refcheck=True): return None + def round(self, decimals=0, out=None): return np.ndarray([0, 0]) + def searchsorted(self, v, side='left', sorter=None): return np.ndarray([0, 0]) + def setfield(self, val, dtype, offset=0): return None + def setflags(self, write=None, align=None, uic=None): return None + def sort(self, axis=-1, kind='quicksort', order=None): return None + def squeeze(self, axis=None): return np.ndarray([0, 0]) + def std(self, axis=None, dtype=None, out=None, ddof=0, keepdims=False): return np.ndarray([0, 0]) + def sum(self, axis=None, dtype=None, out=None, keepdims=False): return np.ndarray([0, 0]) + def swapaxes(self, axis1, axis2): return np.ndarray([0, 0]) + def take(self, indices, axis=None, out=None, mode='raise'): return np.ndarray([0, 0]) + def tobytes(self, order='C'): return b'' + def tofile(self, fid, sep="", format="%s"): return None + def tolist(self, ): return [] + def tostring(self, order='C'): return b'' + def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None): return np.ndarray([0, 0]) + def transpose(self, *axes): return np.ndarray([0, 0]) + def var(self, axis=None, dtype=None, out=None, ddof=0, keepdims=False): return np.ndarray([0, 0]) + def view(self, dtype=None, type=None): return np.ndarray([0, 0]) + """ + node = astroid.extract_node(ndarray) + return node.infer(context=context) + + +def _looks_like_numpy_ndarray(node): + return isinstance(node, astroid.Attribute) and node.attrname == "ndarray" + + +astroid.MANAGER.register_transform( + astroid.Attribute, + astroid.inference_tip(infer_numpy_ndarray), + _looks_like_numpy_ndarray, +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_random_mtrand.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_random_mtrand.py new file mode 100644 index 0000000..cffdcee --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_random_mtrand.py @@ -0,0 +1,70 @@ +# Copyright (c) 2019 hippo91 + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +# TODO(hippo91) : correct the functions return types +"""Astroid hooks for numpy.random.mtrand module.""" + +import astroid + + +def numpy_random_mtrand_transform(): + return astroid.parse( + """ + def beta(a, b, size=None): return uninferable + def binomial(n, p, size=None): return uninferable + def bytes(length): return uninferable + def chisquare(df, size=None): return uninferable + def choice(a, size=None, replace=True, p=None): return uninferable + def dirichlet(alpha, size=None): return uninferable + def exponential(scale=1.0, size=None): return uninferable + def f(dfnum, dfden, size=None): return uninferable + def gamma(shape, scale=1.0, size=None): return uninferable + def geometric(p, size=None): return uninferable + def get_state(): return uninferable + def gumbel(loc=0.0, scale=1.0, size=None): return uninferable + def hypergeometric(ngood, nbad, nsample, size=None): return uninferable + def laplace(loc=0.0, scale=1.0, size=None): return uninferable + def logistic(loc=0.0, scale=1.0, size=None): return uninferable + def lognormal(mean=0.0, sigma=1.0, size=None): return uninferable + def logseries(p, size=None): return uninferable + def multinomial(n, pvals, size=None): return uninferable + def multivariate_normal(mean, cov, size=None): return uninferable + def negative_binomial(n, p, size=None): return uninferable + def noncentral_chisquare(df, nonc, size=None): return uninferable + def noncentral_f(dfnum, dfden, nonc, size=None): return uninferable + def normal(loc=0.0, scale=1.0, size=None): return uninferable + def pareto(a, size=None): return uninferable + def permutation(x): return uninferable + def poisson(lam=1.0, size=None): return uninferable + def power(a, size=None): return uninferable + def rand(*args): return uninferable + def randint(low, high=None, size=None, dtype='l'): + import numpy + return numpy.ndarray((1,1)) + def randn(*args): return uninferable + def random_integers(low, high=None, size=None): return uninferable + def random_sample(size=None): return uninferable + def rayleigh(scale=1.0, size=None): return uninferable + def seed(seed=None): return uninferable + def set_state(state): return uninferable + def shuffle(x): return uninferable + def standard_cauchy(size=None): return uninferable + def standard_exponential(size=None): return uninferable + def standard_gamma(shape, size=None): return uninferable + def standard_normal(size=None): return uninferable + def standard_t(df, size=None): return uninferable + def triangular(left, mode, right, size=None): return uninferable + def uniform(low=0.0, high=1.0, size=None): return uninferable + def vonmises(mu, kappa, size=None): return uninferable + def wald(mean, scale, size=None): return uninferable + def weibull(a, size=None): return uninferable + def zipf(a, size=None): return uninferable + """ + ) + + +astroid.register_module_extender( + astroid.MANAGER, "numpy.random.mtrand", numpy_random_mtrand_transform +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_utils.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_utils.py new file mode 100644 index 0000000..b29d271 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_numpy_utils.py @@ -0,0 +1,65 @@ +# Copyright (c) 2019-2020 hippo91 +# Copyright (c) 2019 Claudiu Popa + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + + +"""Different utilities for the numpy brains""" + + +import astroid + + +def infer_numpy_member(src, node, context=None): + node = astroid.extract_node(src) + return node.infer(context=context) + + +def _is_a_numpy_module(node: astroid.node_classes.Name) -> bool: + """ + Returns True if the node is a representation of a numpy module. + + For example in : + import numpy as np + x = np.linspace(1, 2) + The node is a representation of the numpy module. + + :param node: node to test + :return: True if the node is a representation of the numpy module. + """ + module_nickname = node.name + potential_import_target = [ + x for x in node.lookup(module_nickname)[1] if isinstance(x, astroid.Import) + ] + for target in potential_import_target: + if ("numpy", module_nickname) in target.names: + return True + return False + + +def looks_like_numpy_member( + member_name: str, node: astroid.node_classes.NodeNG +) -> bool: + """ + Returns True if the node is a member of numpy whose + name is member_name. + + :param member_name: name of the member + :param node: node to test + :return: True if the node is a member of numpy + """ + if ( + isinstance(node, astroid.Attribute) + and node.attrname == member_name + and isinstance(node.expr, astroid.Name) + and _is_a_numpy_module(node.expr) + ): + return True + if ( + isinstance(node, astroid.Name) + and node.name == member_name + and node.root().name.startswith("numpy") + ): + return True + return False diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_pkg_resources.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_pkg_resources.py new file mode 100644 index 0000000..25e7649 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_pkg_resources.py @@ -0,0 +1,75 @@ +# Copyright (c) 2016, 2018 Claudiu Popa +# Copyright (c) 2016 Ceridwen + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + + +import astroid +from astroid import parse +from astroid import inference_tip +from astroid import register_module_extender +from astroid import MANAGER + + +def pkg_resources_transform(): + return parse( + """ +def require(*requirements): + return pkg_resources.working_set.require(*requirements) + +def run_script(requires, script_name): + return pkg_resources.working_set.run_script(requires, script_name) + +def iter_entry_points(group, name=None): + return pkg_resources.working_set.iter_entry_points(group, name) + +def resource_exists(package_or_requirement, resource_name): + return get_provider(package_or_requirement).has_resource(resource_name) + +def resource_isdir(package_or_requirement, resource_name): + return get_provider(package_or_requirement).resource_isdir( + resource_name) + +def resource_filename(package_or_requirement, resource_name): + return get_provider(package_or_requirement).get_resource_filename( + self, resource_name) + +def resource_stream(package_or_requirement, resource_name): + return get_provider(package_or_requirement).get_resource_stream( + self, resource_name) + +def resource_string(package_or_requirement, resource_name): + return get_provider(package_or_requirement).get_resource_string( + self, resource_name) + +def resource_listdir(package_or_requirement, resource_name): + return get_provider(package_or_requirement).resource_listdir( + resource_name) + +def extraction_error(): + pass + +def get_cache_path(archive_name, names=()): + extract_path = self.extraction_path or get_default_cache() + target_path = os.path.join(extract_path, archive_name+'-tmp', *names) + return target_path + +def postprocess(tempname, filename): + pass + +def set_extraction_path(path): + pass + +def cleanup_resources(force=False): + pass + +def get_distribution(dist): + return Distribution(dist) + +_namespace_packages = {} +""" + ) + + +register_module_extender(MANAGER, "pkg_resources", pkg_resources_transform) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_pytest.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_pytest.py new file mode 100644 index 0000000..56202ab --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_pytest.py @@ -0,0 +1,88 @@ +# Copyright (c) 2014-2016, 2018 Claudiu Popa +# Copyright (c) 2014 Jeff Quast +# Copyright (c) 2014 Google, Inc. +# Copyright (c) 2016 Florian Bruhin +# Copyright (c) 2016 Ceridwen + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""Astroid hooks for pytest.""" +from __future__ import absolute_import +from astroid import MANAGER, register_module_extender +from astroid.builder import AstroidBuilder + + +def pytest_transform(): + return AstroidBuilder(MANAGER).string_build( + """ + +try: + import _pytest.mark + import _pytest.recwarn + import _pytest.runner + import _pytest.python + import _pytest.skipping + import _pytest.assertion +except ImportError: + pass +else: + deprecated_call = _pytest.recwarn.deprecated_call + warns = _pytest.recwarn.warns + + exit = _pytest.runner.exit + fail = _pytest.runner.fail + skip = _pytest.runner.skip + importorskip = _pytest.runner.importorskip + + xfail = _pytest.skipping.xfail + mark = _pytest.mark.MarkGenerator() + raises = _pytest.python.raises + + # New in pytest 3.0 + try: + approx = _pytest.python.approx + register_assert_rewrite = _pytest.assertion.register_assert_rewrite + except AttributeError: + pass + + +# Moved in pytest 3.0 + +try: + import _pytest.freeze_support + freeze_includes = _pytest.freeze_support.freeze_includes +except ImportError: + try: + import _pytest.genscript + freeze_includes = _pytest.genscript.freeze_includes + except ImportError: + pass + +try: + import _pytest.debugging + set_trace = _pytest.debugging.pytestPDB().set_trace +except ImportError: + try: + import _pytest.pdb + set_trace = _pytest.pdb.pytestPDB().set_trace + except ImportError: + pass + +try: + import _pytest.fixtures + fixture = _pytest.fixtures.fixture + yield_fixture = _pytest.fixtures.yield_fixture +except ImportError: + try: + import _pytest.python + fixture = _pytest.python.fixture + yield_fixture = _pytest.python.yield_fixture + except ImportError: + pass +""" + ) + + +register_module_extender(MANAGER, "pytest", pytest_transform) +register_module_extender(MANAGER, "py.test", pytest_transform) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_qt.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_qt.py new file mode 100644 index 0000000..b703b37 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_qt.py @@ -0,0 +1,83 @@ +# Copyright (c) 2015-2016, 2018 Claudiu Popa +# Copyright (c) 2016 Ceridwen +# Copyright (c) 2017 Roy Wright +# Copyright (c) 2018 Ashley Whetter +# Copyright (c) 2019 Antoine Boellinger + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""Astroid hooks for the PyQT library.""" + +from astroid import MANAGER, register_module_extender +from astroid.builder import AstroidBuilder +from astroid import nodes +from astroid import parse + + +def _looks_like_signal(node, signal_name="pyqtSignal"): + if "__class__" in node.instance_attrs: + try: + cls = node.instance_attrs["__class__"][0] + return cls.name == signal_name + except AttributeError: + # return False if the cls does not have a name attribute + pass + return False + + +def transform_pyqt_signal(node): + module = parse( + """ + class pyqtSignal(object): + def connect(self, slot, type=None, no_receiver_check=False): + pass + def disconnect(self, slot): + pass + def emit(self, *args): + pass + """ + ) + signal_cls = module["pyqtSignal"] + node.instance_attrs["emit"] = signal_cls["emit"] + node.instance_attrs["disconnect"] = signal_cls["disconnect"] + node.instance_attrs["connect"] = signal_cls["connect"] + + +def transform_pyside_signal(node): + module = parse( + """ + class NotPySideSignal(object): + def connect(self, receiver, type=None): + pass + def disconnect(self, receiver): + pass + def emit(self, *args): + pass + """ + ) + signal_cls = module["NotPySideSignal"] + node.instance_attrs["connect"] = signal_cls["connect"] + node.instance_attrs["disconnect"] = signal_cls["disconnect"] + node.instance_attrs["emit"] = signal_cls["emit"] + + +def pyqt4_qtcore_transform(): + return AstroidBuilder(MANAGER).string_build( + """ + +def SIGNAL(signal_name): pass + +class QObject(object): + def emit(self, signal): pass +""" + ) + + +register_module_extender(MANAGER, "PyQt4.QtCore", pyqt4_qtcore_transform) +MANAGER.register_transform(nodes.FunctionDef, transform_pyqt_signal, _looks_like_signal) +MANAGER.register_transform( + nodes.ClassDef, + transform_pyside_signal, + lambda node: node.qname() in ("PySide.QtCore.Signal", "PySide2.QtCore.Signal"), +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_random.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_random.py new file mode 100644 index 0000000..5ec858a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_random.py @@ -0,0 +1,75 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER +import random + +import astroid +from astroid import helpers +from astroid import MANAGER + + +ACCEPTED_ITERABLES_FOR_SAMPLE = (astroid.List, astroid.Set, astroid.Tuple) + + +def _clone_node_with_lineno(node, parent, lineno): + cls = node.__class__ + other_fields = node._other_fields + _astroid_fields = node._astroid_fields + init_params = {"lineno": lineno, "col_offset": node.col_offset, "parent": parent} + postinit_params = {param: getattr(node, param) for param in _astroid_fields} + if other_fields: + init_params.update({param: getattr(node, param) for param in other_fields}) + new_node = cls(**init_params) + if hasattr(node, "postinit") and _astroid_fields: + new_node.postinit(**postinit_params) + return new_node + + +def infer_random_sample(node, context=None): + if len(node.args) != 2: + raise astroid.UseInferenceDefault + + length = node.args[1] + if not isinstance(length, astroid.Const): + raise astroid.UseInferenceDefault + if not isinstance(length.value, int): + raise astroid.UseInferenceDefault + + inferred_sequence = helpers.safe_infer(node.args[0], context=context) + if not inferred_sequence: + raise astroid.UseInferenceDefault + + if not isinstance(inferred_sequence, ACCEPTED_ITERABLES_FOR_SAMPLE): + raise astroid.UseInferenceDefault + + if length.value > len(inferred_sequence.elts): + # In this case, this will raise a ValueError + raise astroid.UseInferenceDefault + + try: + elts = random.sample(inferred_sequence.elts, length.value) + except ValueError: + raise astroid.UseInferenceDefault + + new_node = astroid.List( + lineno=node.lineno, col_offset=node.col_offset, parent=node.scope() + ) + new_elts = [ + _clone_node_with_lineno(elt, parent=new_node, lineno=new_node.lineno) + for elt in elts + ] + new_node.postinit(new_elts) + return iter((new_node,)) + + +def _looks_like_random_sample(node): + func = node.func + if isinstance(func, astroid.Attribute): + return func.attrname == "sample" + if isinstance(func, astroid.Name): + return func.name == "sample" + return False + + +MANAGER.register_transform( + astroid.Call, astroid.inference_tip(infer_random_sample), _looks_like_random_sample +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_re.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_re.py new file mode 100644 index 0000000..c7ee51a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_re.py @@ -0,0 +1,36 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER +import sys +import astroid + +PY36 = sys.version_info >= (3, 6) + +if PY36: + # Since Python 3.6 there is the RegexFlag enum + # where every entry will be exposed via updating globals() + + def _re_transform(): + return astroid.parse( + """ + import sre_compile + ASCII = sre_compile.SRE_FLAG_ASCII + IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE + LOCALE = sre_compile.SRE_FLAG_LOCALE + UNICODE = sre_compile.SRE_FLAG_UNICODE + MULTILINE = sre_compile.SRE_FLAG_MULTILINE + DOTALL = sre_compile.SRE_FLAG_DOTALL + VERBOSE = sre_compile.SRE_FLAG_VERBOSE + A = ASCII + I = IGNORECASE + L = LOCALE + U = UNICODE + M = MULTILINE + S = DOTALL + X = VERBOSE + TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE + T = TEMPLATE + DEBUG = sre_compile.SRE_FLAG_DEBUG + """ + ) + + astroid.register_module_extender(astroid.MANAGER, "re", _re_transform) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_responses.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_responses.py new file mode 100644 index 0000000..3a44129 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_responses.py @@ -0,0 +1,73 @@ +""" +Astroid hooks for responses. + +It might need to be manually updated from the public methods of +:class:`responses.RequestsMock`. + +See: https://github.com/getsentry/responses/blob/master/responses.py + +""" +import astroid + + +def responses_funcs(): + return astroid.parse( + """ + DELETE = "DELETE" + GET = "GET" + HEAD = "HEAD" + OPTIONS = "OPTIONS" + PATCH = "PATCH" + POST = "POST" + PUT = "PUT" + response_callback = None + + def reset(): + return + + def add( + method=None, # method or ``Response`` + url=None, + body="", + adding_headers=None, + *args, + **kwargs + ): + return + + def add_passthru(prefix): + return + + def remove(method_or_response=None, url=None): + return + + def replace(method_or_response=None, url=None, body="", *args, **kwargs): + return + + def add_callback( + method, url, callback, match_querystring=False, content_type="text/plain" + ): + return + + calls = [] + + def __enter__(): + return + + def __exit__(type, value, traceback): + success = type is None + return success + + def activate(func): + return func + + def start(): + return + + def stop(allow_assert=True): + return + """ + ) + + +astroid.register_module_extender(astroid.MANAGER, "responses", responses_funcs) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_scipy_signal.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_scipy_signal.py new file mode 100644 index 0000000..996300d --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_scipy_signal.py @@ -0,0 +1,89 @@ +# Copyright (c) 2019 Valentin Valls + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + + +"""Astroid hooks for scipy.signal module.""" + +import astroid + + +def scipy_signal(): + return astroid.parse( + """ + # different functions defined in scipy.signals + + def barthann(M, sym=True): + return numpy.ndarray([0]) + + def bartlett(M, sym=True): + return numpy.ndarray([0]) + + def blackman(M, sym=True): + return numpy.ndarray([0]) + + def blackmanharris(M, sym=True): + return numpy.ndarray([0]) + + def bohman(M, sym=True): + return numpy.ndarray([0]) + + def boxcar(M, sym=True): + return numpy.ndarray([0]) + + def chebwin(M, at, sym=True): + return numpy.ndarray([0]) + + def cosine(M, sym=True): + return numpy.ndarray([0]) + + def exponential(M, center=None, tau=1.0, sym=True): + return numpy.ndarray([0]) + + def flattop(M, sym=True): + return numpy.ndarray([0]) + + def gaussian(M, std, sym=True): + return numpy.ndarray([0]) + + def general_gaussian(M, p, sig, sym=True): + return numpy.ndarray([0]) + + def hamming(M, sym=True): + return numpy.ndarray([0]) + + def hann(M, sym=True): + return numpy.ndarray([0]) + + def hanning(M, sym=True): + return numpy.ndarray([0]) + + def impulse2(system, X0=None, T=None, N=None, **kwargs): + return numpy.ndarray([0]), numpy.ndarray([0]) + + def kaiser(M, beta, sym=True): + return numpy.ndarray([0]) + + def nuttall(M, sym=True): + return numpy.ndarray([0]) + + def parzen(M, sym=True): + return numpy.ndarray([0]) + + def slepian(M, width, sym=True): + return numpy.ndarray([0]) + + def step2(system, X0=None, T=None, N=None, **kwargs): + return numpy.ndarray([0]), numpy.ndarray([0]) + + def triang(M, sym=True): + return numpy.ndarray([0]) + + def tukey(M, alpha=0.5, sym=True): + return numpy.ndarray([0]) + """ + ) + + +astroid.register_module_extender(astroid.MANAGER, "scipy.signal", scipy_signal) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_six.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_six.py new file mode 100644 index 0000000..46d9fa3 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_six.py @@ -0,0 +1,201 @@ +# Copyright (c) 2014-2016, 2018, 2020 Claudiu Popa +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2018 Bryce Guinta + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + + +"""Astroid hooks for six module.""" + +from textwrap import dedent + +from astroid import MANAGER, register_module_extender +from astroid.builder import AstroidBuilder +from astroid.exceptions import ( + AstroidBuildingError, + InferenceError, + AttributeInferenceError, +) +from astroid import nodes + + +SIX_ADD_METACLASS = "six.add_metaclass" + + +def _indent(text, prefix, predicate=None): + """Adds 'prefix' to the beginning of selected lines in 'text'. + + If 'predicate' is provided, 'prefix' will only be added to the lines + where 'predicate(line)' is True. If 'predicate' is not provided, + it will default to adding 'prefix' to all non-empty lines that do not + consist solely of whitespace characters. + """ + if predicate is None: + predicate = lambda line: line.strip() + + def prefixed_lines(): + for line in text.splitlines(True): + yield prefix + line if predicate(line) else line + + return "".join(prefixed_lines()) + + +_IMPORTS = """ +import _io +cStringIO = _io.StringIO +filter = filter +from itertools import filterfalse +input = input +from sys import intern +map = map +range = range +from importlib import reload +reload_module = lambda module: reload(module) +from functools import reduce +from shlex import quote as shlex_quote +from io import StringIO +from collections import UserDict, UserList, UserString +xrange = range +zip = zip +from itertools import zip_longest +import builtins +import configparser +import copyreg +import _dummy_thread +import http.cookiejar as http_cookiejar +import http.cookies as http_cookies +import html.entities as html_entities +import html.parser as html_parser +import http.client as http_client +import http.server as http_server +BaseHTTPServer = CGIHTTPServer = SimpleHTTPServer = http.server +import pickle as cPickle +import queue +import reprlib +import socketserver +import _thread +import winreg +import xmlrpc.server as xmlrpc_server +import xmlrpc.client as xmlrpc_client +import urllib.robotparser as urllib_robotparser +import email.mime.multipart as email_mime_multipart +import email.mime.nonmultipart as email_mime_nonmultipart +import email.mime.text as email_mime_text +import email.mime.base as email_mime_base +import urllib.parse as urllib_parse +import urllib.error as urllib_error +import tkinter +import tkinter.dialog as tkinter_dialog +import tkinter.filedialog as tkinter_filedialog +import tkinter.scrolledtext as tkinter_scrolledtext +import tkinter.simpledialog as tkinder_simpledialog +import tkinter.tix as tkinter_tix +import tkinter.ttk as tkinter_ttk +import tkinter.constants as tkinter_constants +import tkinter.dnd as tkinter_dnd +import tkinter.colorchooser as tkinter_colorchooser +import tkinter.commondialog as tkinter_commondialog +import tkinter.filedialog as tkinter_tkfiledialog +import tkinter.font as tkinter_font +import tkinter.messagebox as tkinter_messagebox +import urllib +import urllib.request as urllib_request +import urllib.robotparser as urllib_robotparser +import urllib.parse as urllib_parse +import urllib.error as urllib_error +""" + + +def six_moves_transform(): + code = dedent( + """ + class Moves(object): + {} + moves = Moves() + """ + ).format(_indent(_IMPORTS, " ")) + module = AstroidBuilder(MANAGER).string_build(code) + module.name = "six.moves" + return module + + +def _six_fail_hook(modname): + """Fix six.moves imports due to the dynamic nature of this + class. + + Construct a pseudo-module which contains all the necessary imports + for six + + :param modname: Name of failed module + :type modname: str + + :return: An astroid module + :rtype: nodes.Module + """ + + attribute_of = modname != "six.moves" and modname.startswith("six.moves") + if modname != "six.moves" and not attribute_of: + raise AstroidBuildingError(modname=modname) + module = AstroidBuilder(MANAGER).string_build(_IMPORTS) + module.name = "six.moves" + if attribute_of: + # Facilitate import of submodules in Moves + start_index = len(module.name) + attribute = modname[start_index:].lstrip(".").replace(".", "_") + try: + import_attr = module.getattr(attribute)[0] + except AttributeInferenceError: + raise AstroidBuildingError(modname=modname) + if isinstance(import_attr, nodes.Import): + submodule = MANAGER.ast_from_module_name(import_attr.names[0][0]) + return submodule + # Let dummy submodule imports pass through + # This will cause an Uninferable result, which is okay + return module + + +def _looks_like_decorated_with_six_add_metaclass(node): + if not node.decorators: + return False + + for decorator in node.decorators.nodes: + if not isinstance(decorator, nodes.Call): + continue + if decorator.func.as_string() == SIX_ADD_METACLASS: + return True + return False + + +def transform_six_add_metaclass(node): + """Check if the given class node is decorated with *six.add_metaclass* + + If so, inject its argument as the metaclass of the underlying class. + """ + if not node.decorators: + return + + for decorator in node.decorators.nodes: + if not isinstance(decorator, nodes.Call): + continue + + try: + func = next(decorator.func.infer()) + except InferenceError: + continue + if func.qname() == SIX_ADD_METACLASS and decorator.args: + metaclass = decorator.args[0] + node._metaclass = metaclass + return node + + +register_module_extender(MANAGER, "six", six_moves_transform) +register_module_extender( + MANAGER, "requests.packages.urllib3.packages.six", six_moves_transform +) +MANAGER.register_failed_import_hook(_six_fail_hook) +MANAGER.register_transform( + nodes.ClassDef, + transform_six_add_metaclass, + _looks_like_decorated_with_six_add_metaclass, +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_ssl.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_ssl.py new file mode 100644 index 0000000..2ae21c3 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_ssl.py @@ -0,0 +1,75 @@ +# Copyright (c) 2016, 2018 Claudiu Popa +# Copyright (c) 2016 Ceridwen +# Copyright (c) 2019 Benjamin Elven <25181435+S3ntinelX@users.noreply.github.com> + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""Astroid hooks for the ssl library.""" + +from astroid import MANAGER, register_module_extender +from astroid.builder import AstroidBuilder +from astroid import nodes +from astroid import parse + + +def ssl_transform(): + return parse( + """ + from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION + from _ssl import _SSLContext, MemoryBIO + from _ssl import ( + SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError, + SSLSyscallError, SSLEOFError, + ) + from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED + from _ssl import txt2obj as _txt2obj, nid2obj as _nid2obj + from _ssl import RAND_status, RAND_add, RAND_bytes, RAND_pseudo_bytes + try: + from _ssl import RAND_egd + except ImportError: + # LibreSSL does not provide RAND_egd + pass + from _ssl import (OP_ALL, OP_CIPHER_SERVER_PREFERENCE, + OP_NO_COMPRESSION, OP_NO_SSLv2, OP_NO_SSLv3, + OP_NO_TLSv1, OP_NO_TLSv1_1, OP_NO_TLSv1_2, + OP_SINGLE_DH_USE, OP_SINGLE_ECDH_USE) + + from _ssl import (ALERT_DESCRIPTION_ACCESS_DENIED, ALERT_DESCRIPTION_BAD_CERTIFICATE, + ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE, + ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE, + ALERT_DESCRIPTION_BAD_RECORD_MAC, + ALERT_DESCRIPTION_CERTIFICATE_EXPIRED, + ALERT_DESCRIPTION_CERTIFICATE_REVOKED, + ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN, + ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE, + ALERT_DESCRIPTION_CLOSE_NOTIFY, ALERT_DESCRIPTION_DECODE_ERROR, + ALERT_DESCRIPTION_DECOMPRESSION_FAILURE, + ALERT_DESCRIPTION_DECRYPT_ERROR, + ALERT_DESCRIPTION_HANDSHAKE_FAILURE, + ALERT_DESCRIPTION_ILLEGAL_PARAMETER, + ALERT_DESCRIPTION_INSUFFICIENT_SECURITY, + ALERT_DESCRIPTION_INTERNAL_ERROR, + ALERT_DESCRIPTION_NO_RENEGOTIATION, + ALERT_DESCRIPTION_PROTOCOL_VERSION, + ALERT_DESCRIPTION_RECORD_OVERFLOW, + ALERT_DESCRIPTION_UNEXPECTED_MESSAGE, + ALERT_DESCRIPTION_UNKNOWN_CA, + ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY, + ALERT_DESCRIPTION_UNRECOGNIZED_NAME, + ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE, + ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION, + ALERT_DESCRIPTION_USER_CANCELLED) + from _ssl import (SSL_ERROR_EOF, SSL_ERROR_INVALID_ERROR_CODE, SSL_ERROR_SSL, + SSL_ERROR_SYSCALL, SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_READ, + SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_X509_LOOKUP, SSL_ERROR_ZERO_RETURN) + from _ssl import VERIFY_CRL_CHECK_CHAIN, VERIFY_CRL_CHECK_LEAF, VERIFY_DEFAULT, VERIFY_X509_STRICT + from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN + from _ssl import _OPENSSL_API_VERSION + from _ssl import PROTOCOL_SSLv23, PROTOCOL_TLSv1, PROTOCOL_TLSv1_1, PROTOCOL_TLSv1_2 + from _ssl import PROTOCOL_TLS, PROTOCOL_TLS_CLIENT, PROTOCOL_TLS_SERVER + """ + ) + + +register_module_extender(MANAGER, "ssl", ssl_transform) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_subprocess.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_subprocess.py new file mode 100644 index 0000000..ab7d5d7 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_subprocess.py @@ -0,0 +1,146 @@ +# Copyright (c) 2016-2020 Claudiu Popa +# Copyright (c) 2017 Hugo +# Copyright (c) 2018 Peter Talley +# Copyright (c) 2018 Bryce Guinta +# Copyright (c) 2019 Hugo van Kemenade + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +import sys +import textwrap + +import astroid + + +PY37 = sys.version_info >= (3, 7) +PY36 = sys.version_info >= (3, 6) + + +def _subprocess_transform(): + communicate = (bytes("string", "ascii"), bytes("string", "ascii")) + communicate_signature = "def communicate(self, input=None, timeout=None)" + if PY37: + init = """ + def __init__(self, args, bufsize=0, executable=None, + stdin=None, stdout=None, stderr=None, + preexec_fn=None, close_fds=False, shell=False, + cwd=None, env=None, universal_newlines=False, + startupinfo=None, creationflags=0, restore_signals=True, + start_new_session=False, pass_fds=(), *, + encoding=None, errors=None, text=None): + pass + """ + elif PY36: + init = """ + def __init__(self, args, bufsize=0, executable=None, + stdin=None, stdout=None, stderr=None, + preexec_fn=None, close_fds=False, shell=False, + cwd=None, env=None, universal_newlines=False, + startupinfo=None, creationflags=0, restore_signals=True, + start_new_session=False, pass_fds=(), *, + encoding=None, errors=None): + pass + """ + else: + init = """ + def __init__(self, args, bufsize=0, executable=None, + stdin=None, stdout=None, stderr=None, + preexec_fn=None, close_fds=False, shell=False, + cwd=None, env=None, universal_newlines=False, + startupinfo=None, creationflags=0, restore_signals=True, + start_new_session=False, pass_fds=()): + pass + """ + wait_signature = "def wait(self, timeout=None)" + ctx_manager = """ + def __enter__(self): return self + def __exit__(self, *args): pass + """ + py3_args = "args = []" + + if PY37: + check_output_signature = """ + check_output( + args, *, + stdin=None, + stderr=None, + shell=False, + cwd=None, + encoding=None, + errors=None, + universal_newlines=False, + timeout=None, + env=None, + text=None, + restore_signals=True, + preexec_fn=None, + pass_fds=(), + input=None, + start_new_session=False + ): + """.strip() + else: + check_output_signature = """ + check_output( + args, *, + stdin=None, + stderr=None, + shell=False, + cwd=None, + encoding=None, + errors=None, + universal_newlines=False, + timeout=None, + env=None, + restore_signals=True, + preexec_fn=None, + pass_fds=(), + input=None, + start_new_session=False + ): + """.strip() + + code = textwrap.dedent( + """ + def %(check_output_signature)s + if universal_newlines: + return "" + return b"" + + class Popen(object): + returncode = pid = 0 + stdin = stdout = stderr = file() + %(py3_args)s + + %(communicate_signature)s: + return %(communicate)r + %(wait_signature)s: + return self.returncode + def poll(self): + return self.returncode + def send_signal(self, signal): + pass + def terminate(self): + pass + def kill(self): + pass + %(ctx_manager)s + """ + % { + "check_output_signature": check_output_signature, + "communicate": communicate, + "communicate_signature": communicate_signature, + "wait_signature": wait_signature, + "ctx_manager": ctx_manager, + "py3_args": py3_args, + } + ) + + init_lines = textwrap.dedent(init).splitlines() + indented_init = "\n".join(" " * 4 + line for line in init_lines) + code += indented_init + return astroid.parse(code) + + +astroid.register_module_extender(astroid.MANAGER, "subprocess", _subprocess_transform) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_threading.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_threading.py new file mode 100644 index 0000000..ba3085b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_threading.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2016, 2018-2019 Claudiu Popa +# Copyright (c) 2017 Łukasz Rogalski + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER +import astroid + + +def _thread_transform(): + return astroid.parse( + """ + class lock(object): + def acquire(self, blocking=True, timeout=-1): + return False + def release(self): + pass + def __enter__(self): + return True + def __exit__(self, *args): + pass + def locked(self): + return False + + def Lock(): + return lock() + """ + ) + + +astroid.register_module_extender(astroid.MANAGER, "threading", _thread_transform) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_typing.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_typing.py new file mode 100644 index 0000000..9ff7227 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_typing.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017-2018 Claudiu Popa +# Copyright (c) 2017 Łukasz Rogalski +# Copyright (c) 2017 David Euresti +# Copyright (c) 2018 Bryce Guinta + +"""Astroid hooks for typing.py support.""" +import typing + +from astroid import ( + MANAGER, + UseInferenceDefault, + extract_node, + inference_tip, + nodes, + InferenceError, +) + + +TYPING_NAMEDTUPLE_BASENAMES = {"NamedTuple", "typing.NamedTuple"} +TYPING_TYPEVARS = {"TypeVar", "NewType"} +TYPING_TYPEVARS_QUALIFIED = {"typing.TypeVar", "typing.NewType"} +TYPING_TYPE_TEMPLATE = """ +class Meta(type): + def __getitem__(self, item): + return self + + @property + def __args__(self): + return () + +class {0}(metaclass=Meta): + pass +""" +TYPING_MEMBERS = set(typing.__all__) + + +def looks_like_typing_typevar_or_newtype(node): + func = node.func + if isinstance(func, nodes.Attribute): + return func.attrname in TYPING_TYPEVARS + if isinstance(func, nodes.Name): + return func.name in TYPING_TYPEVARS + return False + + +def infer_typing_typevar_or_newtype(node, context=None): + """Infer a typing.TypeVar(...) or typing.NewType(...) call""" + try: + func = next(node.func.infer(context=context)) + except InferenceError as exc: + raise UseInferenceDefault from exc + + if func.qname() not in TYPING_TYPEVARS_QUALIFIED: + raise UseInferenceDefault + if not node.args: + raise UseInferenceDefault + + typename = node.args[0].as_string().strip("'") + node = extract_node(TYPING_TYPE_TEMPLATE.format(typename)) + return node.infer(context=context) + + +def _looks_like_typing_subscript(node): + """Try to figure out if a Subscript node *might* be a typing-related subscript""" + if isinstance(node, nodes.Name): + return node.name in TYPING_MEMBERS + elif isinstance(node, nodes.Attribute): + return node.attrname in TYPING_MEMBERS + elif isinstance(node, nodes.Subscript): + return _looks_like_typing_subscript(node.value) + return False + + +def infer_typing_attr(node, context=None): + """Infer a typing.X[...] subscript""" + try: + value = next(node.value.infer()) + except InferenceError as exc: + raise UseInferenceDefault from exc + + if not value.qname().startswith("typing."): + raise UseInferenceDefault + + node = extract_node(TYPING_TYPE_TEMPLATE.format(value.qname().split(".")[-1])) + return node.infer(context=context) + + +MANAGER.register_transform( + nodes.Call, + inference_tip(infer_typing_typevar_or_newtype), + looks_like_typing_typevar_or_newtype, +) +MANAGER.register_transform( + nodes.Subscript, inference_tip(infer_typing_attr), _looks_like_typing_subscript +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_uuid.py b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_uuid.py new file mode 100644 index 0000000..5a33fc2 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/brain/brain_uuid.py @@ -0,0 +1,20 @@ +# Copyright (c) 2017-2018 Claudiu Popa + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""Astroid hooks for the UUID module.""" + + +from astroid import MANAGER +from astroid import nodes + + +def _patch_uuid_class(node): + # The .int member is patched using __dict__ + node.locals["int"] = [nodes.Const(0, parent=node)] + + +MANAGER.register_transform( + nodes.ClassDef, _patch_uuid_class, lambda node: node.qname() == "uuid.UUID" +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/builder.py b/Display/.venv/lib/python3.7/site-packages/astroid/builder.py new file mode 100644 index 0000000..142764b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/builder.py @@ -0,0 +1,455 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2013 Phil Schaf +# Copyright (c) 2014-2019 Claudiu Popa +# Copyright (c) 2014-2015 Google, Inc. +# Copyright (c) 2014 Alexander Presnyakov +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2016 Derek Gustafson +# Copyright (c) 2017 Łukasz Rogalski +# Copyright (c) 2018 Anthony Sottile + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""The AstroidBuilder makes astroid from living object and / or from _ast + +The builder is not thread safe and can't be used to parse different sources +at the same time. +""" + +import os +import textwrap +from tokenize import detect_encoding + +from astroid._ast import get_parser_module +from astroid import bases +from astroid import exceptions +from astroid import manager +from astroid import modutils +from astroid import raw_building +from astroid import rebuilder +from astroid import nodes +from astroid import util + +objects = util.lazy_import("objects") + +# The name of the transient function that is used to +# wrap expressions to be extracted when calling +# extract_node. +_TRANSIENT_FUNCTION = "__" + +# The comment used to select a statement to be extracted +# when calling extract_node. +_STATEMENT_SELECTOR = "#@" +MISPLACED_TYPE_ANNOTATION_ERROR = "misplaced type annotation" +MANAGER = manager.AstroidManager() + + +def open_source_file(filename): + with open(filename, "rb") as byte_stream: + encoding = detect_encoding(byte_stream.readline)[0] + stream = open(filename, "r", newline=None, encoding=encoding) + data = stream.read() + return stream, encoding, data + + +def _can_assign_attr(node, attrname): + try: + slots = node.slots() + except NotImplementedError: + pass + else: + if slots and attrname not in {slot.value for slot in slots}: + return False + return True + + +class AstroidBuilder(raw_building.InspectBuilder): + """Class for building an astroid tree from source code or from a live module. + + The param *manager* specifies the manager class which should be used. + If no manager is given, then the default one will be used. The + param *apply_transforms* determines if the transforms should be + applied after the tree was built from source or from a live object, + by default being True. + """ + + # pylint: disable=redefined-outer-name + def __init__(self, manager=None, apply_transforms=True): + super().__init__() + self._manager = manager or MANAGER + self._apply_transforms = apply_transforms + + def module_build(self, module, modname=None): + """Build an astroid from a living module instance.""" + node = None + path = getattr(module, "__file__", None) + if path is not None: + path_, ext = os.path.splitext(modutils._path_from_filename(path)) + if ext in (".py", ".pyc", ".pyo") and os.path.exists(path_ + ".py"): + node = self.file_build(path_ + ".py", modname) + if node is None: + # this is a built-in module + # get a partial representation by introspection + node = self.inspect_build(module, modname=modname, path=path) + if self._apply_transforms: + # We have to handle transformation by ourselves since the + # rebuilder isn't called for builtin nodes + node = self._manager.visit_transforms(node) + return node + + def file_build(self, path, modname=None): + """Build astroid from a source code file (i.e. from an ast) + + *path* is expected to be a python source file + """ + try: + stream, encoding, data = open_source_file(path) + except IOError as exc: + raise exceptions.AstroidBuildingError( + "Unable to load file {path}:\n{error}", + modname=modname, + path=path, + error=exc, + ) from exc + except (SyntaxError, LookupError) as exc: + raise exceptions.AstroidSyntaxError( + "Python 3 encoding specification error or unknown encoding:\n" + "{error}", + modname=modname, + path=path, + error=exc, + ) from exc + except UnicodeError as exc: # wrong encoding + # detect_encoding returns utf-8 if no encoding specified + raise exceptions.AstroidBuildingError( + "Wrong or no encoding specified for {filename}.", filename=path + ) from exc + with stream: + # get module name if necessary + if modname is None: + try: + modname = ".".join(modutils.modpath_from_file(path)) + except ImportError: + modname = os.path.splitext(os.path.basename(path))[0] + # build astroid representation + module = self._data_build(data, modname, path) + return self._post_build(module, encoding) + + def string_build(self, data, modname="", path=None): + """Build astroid from source code string.""" + module = self._data_build(data, modname, path) + module.file_bytes = data.encode("utf-8") + return self._post_build(module, "utf-8") + + def _post_build(self, module, encoding): + """Handles encoding and delayed nodes after a module has been built""" + module.file_encoding = encoding + self._manager.cache_module(module) + # post tree building steps after we stored the module in the cache: + for from_node in module._import_from_nodes: + if from_node.modname == "__future__": + for symbol, _ in from_node.names: + module.future_imports.add(symbol) + self.add_from_names_to_locals(from_node) + # handle delayed assattr nodes + for delayed in module._delayed_assattr: + self.delayed_assattr(delayed) + + # Visit the transforms + if self._apply_transforms: + module = self._manager.visit_transforms(module) + return module + + def _data_build(self, data, modname, path): + """Build tree node from data and add some informations""" + try: + node, parser_module = _parse_string(data, type_comments=True) + except (TypeError, ValueError, SyntaxError) as exc: + raise exceptions.AstroidSyntaxError( + "Parsing Python code failed:\n{error}", + source=data, + modname=modname, + path=path, + error=exc, + ) from exc + + if path is not None: + node_file = os.path.abspath(path) + else: + node_file = "" + if modname.endswith(".__init__"): + modname = modname[:-9] + package = True + else: + package = ( + path is not None + and os.path.splitext(os.path.basename(path))[0] == "__init__" + ) + builder = rebuilder.TreeRebuilder(self._manager, parser_module) + module = builder.visit_module(node, modname, node_file, package) + module._import_from_nodes = builder._import_from_nodes + module._delayed_assattr = builder._delayed_assattr + return module + + def add_from_names_to_locals(self, node): + """Store imported names to the locals + + Resort the locals if coming from a delayed node + """ + _key_func = lambda node: node.fromlineno + + def sort_locals(my_list): + my_list.sort(key=_key_func) + + for (name, asname) in node.names: + if name == "*": + try: + imported = node.do_import_module() + except exceptions.AstroidBuildingError: + continue + for name in imported.public_names(): + node.parent.set_local(name, node) + sort_locals(node.parent.scope().locals[name]) + else: + node.parent.set_local(asname or name, node) + sort_locals(node.parent.scope().locals[asname or name]) + + def delayed_assattr(self, node): + """Visit a AssAttr node + + This adds name to locals and handle members definition. + """ + try: + frame = node.frame() + for inferred in node.expr.infer(): + if inferred is util.Uninferable: + continue + try: + cls = inferred.__class__ + if cls is bases.Instance or cls is objects.ExceptionInstance: + inferred = inferred._proxied + iattrs = inferred.instance_attrs + if not _can_assign_attr(inferred, node.attrname): + continue + elif isinstance(inferred, bases.Instance): + # Const, Tuple or other containers that inherit from + # `Instance` + continue + elif inferred.is_function: + iattrs = inferred.instance_attrs + else: + iattrs = inferred.locals + except AttributeError: + # XXX log error + continue + values = iattrs.setdefault(node.attrname, []) + if node in values: + continue + # get assign in __init__ first XXX useful ? + if ( + frame.name == "__init__" + and values + and values[0].frame().name != "__init__" + ): + values.insert(0, node) + else: + values.append(node) + except exceptions.InferenceError: + pass + + +def build_namespace_package_module(name, path): + return nodes.Module(name, doc="", path=path, package=True) + + +def parse(code, module_name="", path=None, apply_transforms=True): + """Parses a source string in order to obtain an astroid AST from it + + :param str code: The code for the module. + :param str module_name: The name for the module, if any + :param str path: The path for the module + :param bool apply_transforms: + Apply the transforms for the give code. Use it if you + don't want the default transforms to be applied. + """ + code = textwrap.dedent(code) + builder = AstroidBuilder(manager=MANAGER, apply_transforms=apply_transforms) + return builder.string_build(code, modname=module_name, path=path) + + +def _extract_expressions(node): + """Find expressions in a call to _TRANSIENT_FUNCTION and extract them. + + The function walks the AST recursively to search for expressions that + are wrapped into a call to _TRANSIENT_FUNCTION. If it finds such an + expression, it completely removes the function call node from the tree, + replacing it by the wrapped expression inside the parent. + + :param node: An astroid node. + :type node: astroid.bases.NodeNG + :yields: The sequence of wrapped expressions on the modified tree + expression can be found. + """ + if ( + isinstance(node, nodes.Call) + and isinstance(node.func, nodes.Name) + and node.func.name == _TRANSIENT_FUNCTION + ): + real_expr = node.args[0] + real_expr.parent = node.parent + # Search for node in all _astng_fields (the fields checked when + # get_children is called) of its parent. Some of those fields may + # be lists or tuples, in which case the elements need to be checked. + # When we find it, replace it by real_expr, so that the AST looks + # like no call to _TRANSIENT_FUNCTION ever took place. + for name in node.parent._astroid_fields: + child = getattr(node.parent, name) + if isinstance(child, (list, tuple)): + for idx, compound_child in enumerate(child): + if compound_child is node: + child[idx] = real_expr + elif child is node: + setattr(node.parent, name, real_expr) + yield real_expr + else: + for child in node.get_children(): + yield from _extract_expressions(child) + + +def _find_statement_by_line(node, line): + """Extracts the statement on a specific line from an AST. + + If the line number of node matches line, it will be returned; + otherwise its children are iterated and the function is called + recursively. + + :param node: An astroid node. + :type node: astroid.bases.NodeNG + :param line: The line number of the statement to extract. + :type line: int + :returns: The statement on the line, or None if no statement for the line + can be found. + :rtype: astroid.bases.NodeNG or None + """ + if isinstance(node, (nodes.ClassDef, nodes.FunctionDef)): + # This is an inaccuracy in the AST: the nodes that can be + # decorated do not carry explicit information on which line + # the actual definition (class/def), but .fromline seems to + # be close enough. + node_line = node.fromlineno + else: + node_line = node.lineno + + if node_line == line: + return node + + for child in node.get_children(): + result = _find_statement_by_line(child, line) + if result: + return result + + return None + + +def extract_node(code, module_name=""): + """Parses some Python code as a module and extracts a designated AST node. + + Statements: + To extract one or more statement nodes, append #@ to the end of the line + + Examples: + >>> def x(): + >>> def y(): + >>> return 1 #@ + + The return statement will be extracted. + + >>> class X(object): + >>> def meth(self): #@ + >>> pass + + The function object 'meth' will be extracted. + + Expressions: + To extract arbitrary expressions, surround them with the fake + function call __(...). After parsing, the surrounded expression + will be returned and the whole AST (accessible via the returned + node's parent attribute) will look like the function call was + never there in the first place. + + Examples: + >>> a = __(1) + + The const node will be extracted. + + >>> def x(d=__(foo.bar)): pass + + The node containing the default argument will be extracted. + + >>> def foo(a, b): + >>> return 0 < __(len(a)) < b + + The node containing the function call 'len' will be extracted. + + If no statements or expressions are selected, the last toplevel + statement will be returned. + + If the selected statement is a discard statement, (i.e. an expression + turned into a statement), the wrapped expression is returned instead. + + For convenience, singleton lists are unpacked. + + :param str code: A piece of Python code that is parsed as + a module. Will be passed through textwrap.dedent first. + :param str module_name: The name of the module. + :returns: The designated node from the parse tree, or a list of nodes. + :rtype: astroid.bases.NodeNG, or a list of nodes. + """ + + def _extract(node): + if isinstance(node, nodes.Expr): + return node.value + + return node + + requested_lines = [] + for idx, line in enumerate(code.splitlines()): + if line.strip().endswith(_STATEMENT_SELECTOR): + requested_lines.append(idx + 1) + + tree = parse(code, module_name=module_name) + if not tree.body: + raise ValueError("Empty tree, cannot extract from it") + + extracted = [] + if requested_lines: + extracted = [_find_statement_by_line(tree, line) for line in requested_lines] + + # Modifies the tree. + extracted.extend(_extract_expressions(tree)) + + if not extracted: + extracted.append(tree.body[-1]) + + extracted = [_extract(node) for node in extracted] + if len(extracted) == 1: + return extracted[0] + return extracted + + +def _parse_string(data, type_comments=True): + parser_module = get_parser_module(type_comments=type_comments) + try: + parsed = parser_module.parse(data + "\n", type_comments=type_comments) + except SyntaxError as exc: + # If the type annotations are misplaced for some reason, we do not want + # to fail the entire parsing of the file, so we need to retry the parsing without + # type comment support. + if exc.args[0] != MISPLACED_TYPE_ANNOTATION_ERROR or not type_comments: + raise + + parser_module = get_parser_module(type_comments=False) + parsed = parser_module.parse(data + "\n", type_comments=False) + return parsed, parser_module diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/context.py b/Display/.venv/lib/python3.7/site-packages/astroid/context.py new file mode 100644 index 0000000..40cebf2 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/context.py @@ -0,0 +1,179 @@ +# Copyright (c) 2015-2016, 2018-2019 Claudiu Popa +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2018 Bryce Guinta +# Copyright (c) 2018 Nick Drozd + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""Various context related utilities, including inference and call contexts.""" +import contextlib +import pprint +from typing import Optional + + +class InferenceContext: + """Provide context for inference + + Store already inferred nodes to save time + Account for already visited nodes to infinite stop infinite recursion + """ + + __slots__ = ( + "path", + "lookupname", + "callcontext", + "boundnode", + "inferred", + "extra_context", + ) + + def __init__(self, path=None, inferred=None): + self.path = path or set() + """ + :type: set(tuple(NodeNG, optional(str))) + + Path of visited nodes and their lookupname + + Currently this key is ``(node, context.lookupname)`` + """ + self.lookupname = None + """ + :type: optional[str] + + The original name of the node + + e.g. + foo = 1 + The inference of 'foo' is nodes.Const(1) but the lookup name is 'foo' + """ + self.callcontext = None + """ + :type: optional[CallContext] + + The call arguments and keywords for the given context + """ + self.boundnode = None + """ + :type: optional[NodeNG] + + The bound node of the given context + + e.g. the bound node of object.__new__(cls) is the object node + """ + self.inferred = inferred or {} + """ + :type: dict(seq, seq) + + Inferred node contexts to their mapped results + Currently the key is ``(node, lookupname, callcontext, boundnode)`` + and the value is tuple of the inferred results + """ + self.extra_context = {} + """ + :type: dict(NodeNG, Context) + + Context that needs to be passed down through call stacks + for call arguments + """ + + def push(self, node): + """Push node into inference path + + :return: True if node is already in context path else False + :rtype: bool + + Allows one to see if the given node has already + been looked at for this inference context""" + name = self.lookupname + if (node, name) in self.path: + return True + + self.path.add((node, name)) + return False + + def clone(self): + """Clone inference path + + For example, each side of a binary operation (BinOp) + starts with the same context but diverge as each side is inferred + so the InferenceContext will need be cloned""" + # XXX copy lookupname/callcontext ? + clone = InferenceContext(self.path, inferred=self.inferred) + clone.callcontext = self.callcontext + clone.boundnode = self.boundnode + clone.extra_context = self.extra_context + return clone + + def cache_generator(self, key, generator): + """Cache result of generator into dictionary + + Used to cache inference results""" + results = [] + for result in generator: + results.append(result) + yield result + + self.inferred[key] = tuple(results) + + @contextlib.contextmanager + def restore_path(self): + path = set(self.path) + yield + self.path = path + + def __str__(self): + state = ( + "%s=%s" + % (field, pprint.pformat(getattr(self, field), width=80 - len(field))) + for field in self.__slots__ + ) + return "%s(%s)" % (type(self).__name__, ",\n ".join(state)) + + +class CallContext: + """Holds information for a call site.""" + + __slots__ = ("args", "keywords") + + def __init__(self, args, keywords=None): + """ + :param List[NodeNG] args: Call positional arguments + :param Union[List[nodes.Keyword], None] keywords: Call keywords + """ + self.args = args + if keywords: + keywords = [(arg.arg, arg.value) for arg in keywords] + else: + keywords = [] + self.keywords = keywords + + +def copy_context(context: Optional[InferenceContext]) -> InferenceContext: + """Clone a context if given, or return a fresh contexxt""" + if context is not None: + return context.clone() + + return InferenceContext() + + +def bind_context_to_node(context, node): + """Give a context a boundnode + to retrieve the correct function name or attribute value + with from further inference. + + Do not use an existing context since the boundnode could then + be incorrectly propagated higher up in the call stack. + + :param context: Context to use + :type context: Optional(context) + + :param node: Node to do name lookups from + :type node NodeNG: + + :returns: A new context + :rtype: InferenceContext + """ + context = copy_context(context) + context.boundnode = node + return context diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/decorators.py b/Display/.venv/lib/python3.7/site-packages/astroid/decorators.py new file mode 100644 index 0000000..0f3632c --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/decorators.py @@ -0,0 +1,142 @@ +# Copyright (c) 2015-2016, 2018 Claudiu Popa +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2015 Florian Bruhin +# Copyright (c) 2016 Derek Gustafson +# Copyright (c) 2018 Nick Drozd +# Copyright (c) 2018 Tomas Gavenciak +# Copyright (c) 2018 Ashley Whetter +# Copyright (c) 2018 HoverHell +# Copyright (c) 2018 Bryce Guinta + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +""" A few useful function/method decorators.""" + +import functools + +import wrapt + +from astroid import context as contextmod +from astroid import exceptions +from astroid import util + + +@wrapt.decorator +def cached(func, instance, args, kwargs): + """Simple decorator to cache result of method calls without args.""" + cache = getattr(instance, "__cache", None) + if cache is None: + instance.__cache = cache = {} + try: + return cache[func] + except KeyError: + cache[func] = result = func(*args, **kwargs) + return result + + +class cachedproperty: + """ Provides a cached property equivalent to the stacking of + @cached and @property, but more efficient. + + After first usage, the becomes part of the object's + __dict__. Doing: + + del obj. empties the cache. + + Idea taken from the pyramid_ framework and the mercurial_ project. + + .. _pyramid: http://pypi.python.org/pypi/pyramid + .. _mercurial: http://pypi.python.org/pypi/Mercurial + """ + + __slots__ = ("wrapped",) + + def __init__(self, wrapped): + try: + wrapped.__name__ + except AttributeError as exc: + raise TypeError("%s must have a __name__ attribute" % wrapped) from exc + self.wrapped = wrapped + + @property + def __doc__(self): + doc = getattr(self.wrapped, "__doc__", None) + return "%s" % ( + "\n%s" % doc if doc else "" + ) + + def __get__(self, inst, objtype=None): + if inst is None: + return self + val = self.wrapped(inst) + setattr(inst, self.wrapped.__name__, val) + return val + + +def path_wrapper(func): + """return the given infer function wrapped to handle the path + + Used to stop inference if the node has already been looked + at for a given `InferenceContext` to prevent infinite recursion + """ + + @functools.wraps(func) + def wrapped(node, context=None, _func=func, **kwargs): + """wrapper function handling context""" + if context is None: + context = contextmod.InferenceContext() + if context.push(node): + return None + + yielded = set() + generator = _func(node, context, **kwargs) + try: + while True: + res = next(generator) + # unproxy only true instance, not const, tuple, dict... + if res.__class__.__name__ == "Instance": + ares = res._proxied + else: + ares = res + if ares not in yielded: + yield res + yielded.add(ares) + except StopIteration as error: + if error.args: + return error.args[0] + return None + + return wrapped + + +@wrapt.decorator +def yes_if_nothing_inferred(func, instance, args, kwargs): + generator = func(*args, **kwargs) + + try: + yield next(generator) + except StopIteration: + # generator is empty + yield util.Uninferable + return + + yield from generator + + +@wrapt.decorator +def raise_if_nothing_inferred(func, instance, args, kwargs): + generator = func(*args, **kwargs) + + try: + yield next(generator) + except StopIteration as error: + # generator is empty + if error.args: + # pylint: disable=not-a-mapping + raise exceptions.InferenceError(**error.args[0]) + raise exceptions.InferenceError( + "StopIteration raised without any error information." + ) + + yield from generator diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/exceptions.py b/Display/.venv/lib/python3.7/site-packages/astroid/exceptions.py new file mode 100644 index 0000000..08e72c1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/exceptions.py @@ -0,0 +1,230 @@ +# Copyright (c) 2007, 2009-2010, 2013 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2014 Google, Inc. +# Copyright (c) 2015-2018 Claudiu Popa +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2016 Derek Gustafson +# Copyright (c) 2018 Bryce Guinta + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""this module contains exceptions used in the astroid library +""" +from astroid import util + + +class AstroidError(Exception): + """base exception class for all astroid related exceptions + + AstroidError and its subclasses are structured, intended to hold + objects representing state when the exception is thrown. Field + values are passed to the constructor as keyword-only arguments. + Each subclass has its own set of standard fields, but use your + best judgment to decide whether a specific exception instance + needs more or fewer fields for debugging. Field values may be + used to lazily generate the error message: self.message.format() + will be called with the field names and values supplied as keyword + arguments. + """ + + def __init__(self, message="", **kws): + super().__init__(message) + self.message = message + for key, value in kws.items(): + setattr(self, key, value) + + def __str__(self): + return self.message.format(**vars(self)) + + +class AstroidBuildingError(AstroidError): + """exception class when we are unable to build an astroid representation + + Standard attributes: + modname: Name of the module that AST construction failed for. + error: Exception raised during construction. + """ + + def __init__(self, message="Failed to import module {modname}.", **kws): + super().__init__(message, **kws) + + +class AstroidImportError(AstroidBuildingError): + """Exception class used when a module can't be imported by astroid.""" + + +class TooManyLevelsError(AstroidImportError): + """Exception class which is raised when a relative import was beyond the top-level. + + Standard attributes: + level: The level which was attempted. + name: the name of the module on which the relative import was attempted. + """ + + level = None + name = None + + def __init__( + self, + message="Relative import with too many levels " "({level}) for module {name!r}", + **kws + ): + super().__init__(message, **kws) + + +class AstroidSyntaxError(AstroidBuildingError): + """Exception class used when a module can't be parsed.""" + + +class NoDefault(AstroidError): + """raised by function's `default_value` method when an argument has + no default value + + Standard attributes: + func: Function node. + name: Name of argument without a default. + """ + + func = None + name = None + + def __init__(self, message="{func!r} has no default for {name!r}.", **kws): + super().__init__(message, **kws) + + +class ResolveError(AstroidError): + """Base class of astroid resolution/inference error. + + ResolveError is not intended to be raised. + + Standard attributes: + context: InferenceContext object. + """ + + context = None + + +class MroError(ResolveError): + """Error raised when there is a problem with method resolution of a class. + + Standard attributes: + mros: A sequence of sequences containing ClassDef nodes. + cls: ClassDef node whose MRO resolution failed. + context: InferenceContext object. + """ + + mros = () + cls = None + + def __str__(self): + mro_names = ", ".join( + "({})".format(", ".join(b.name for b in m)) for m in self.mros + ) + return self.message.format(mros=mro_names, cls=self.cls) + + +class DuplicateBasesError(MroError): + """Error raised when there are duplicate bases in the same class bases.""" + + +class InconsistentMroError(MroError): + """Error raised when a class's MRO is inconsistent.""" + + +class SuperError(ResolveError): + """Error raised when there is a problem with a *super* call. + + Standard attributes: + *super_*: The Super instance that raised the exception. + context: InferenceContext object. + """ + + super_ = None + + def __str__(self): + return self.message.format(**vars(self.super_)) + + +class InferenceError(ResolveError): + """raised when we are unable to infer a node + + Standard attributes: + node: The node inference was called on. + context: InferenceContext object. + """ + + node = None + context = None + + def __init__(self, message="Inference failed for {node!r}.", **kws): + super().__init__(message, **kws) + + +# Why does this inherit from InferenceError rather than ResolveError? +# Changing it causes some inference tests to fail. +class NameInferenceError(InferenceError): + """Raised when a name lookup fails, corresponds to NameError. + + Standard attributes: + name: The name for which lookup failed, as a string. + scope: The node representing the scope in which the lookup occurred. + context: InferenceContext object. + """ + + name = None + scope = None + + def __init__(self, message="{name!r} not found in {scope!r}.", **kws): + super().__init__(message, **kws) + + +class AttributeInferenceError(ResolveError): + """Raised when an attribute lookup fails, corresponds to AttributeError. + + Standard attributes: + target: The node for which lookup failed. + attribute: The attribute for which lookup failed, as a string. + context: InferenceContext object. + """ + + target = None + attribute = None + + def __init__(self, message="{attribute!r} not found on {target!r}.", **kws): + super().__init__(message, **kws) + + +class UseInferenceDefault(Exception): + """exception to be raised in custom inference function to indicate that it + should go back to the default behaviour + """ + + +class _NonDeducibleTypeHierarchy(Exception): + """Raised when is_subtype / is_supertype can't deduce the relation between two types.""" + + +class AstroidIndexError(AstroidError): + """Raised when an Indexable / Mapping does not have an index / key.""" + + +class AstroidTypeError(AstroidError): + """Raised when a TypeError would be expected in Python code.""" + + +class InferenceOverwriteError(AstroidError): + """Raised when an inference tip is overwritten + + Currently only used for debugging. + """ + + +# Backwards-compatibility aliases +OperationError = util.BadOperationMessage +UnaryOperationError = util.BadUnaryOperationMessage +BinaryOperationError = util.BadBinaryOperationMessage + +SuperArgumentTypeError = SuperError +UnresolvableName = NameInferenceError +NotFoundError = AttributeInferenceError +AstroidBuildingException = AstroidBuildingError diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/helpers.py b/Display/.venv/lib/python3.7/site-packages/astroid/helpers.py new file mode 100644 index 0000000..1c84651 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/helpers.py @@ -0,0 +1,282 @@ +# Copyright (c) 2015-2020 Claudiu Popa +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2018 Bryce Guinta + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + + +""" +Various helper utilities. +""" + +import builtins as builtins_mod + +from astroid import bases +from astroid import context as contextmod +from astroid import exceptions +from astroid import manager +from astroid import nodes +from astroid import raw_building +from astroid import scoped_nodes +from astroid import util + + +BUILTINS = builtins_mod.__name__ + + +def _build_proxy_class(cls_name, builtins): + proxy = raw_building.build_class(cls_name) + proxy.parent = builtins + return proxy + + +def _function_type(function, builtins): + if isinstance(function, scoped_nodes.Lambda): + if function.root().name == BUILTINS: + cls_name = "builtin_function_or_method" + else: + cls_name = "function" + elif isinstance(function, bases.BoundMethod): + cls_name = "method" + elif isinstance(function, bases.UnboundMethod): + cls_name = "function" + return _build_proxy_class(cls_name, builtins) + + +def _object_type(node, context=None): + astroid_manager = manager.AstroidManager() + builtins = astroid_manager.builtins_module + context = context or contextmod.InferenceContext() + + for inferred in node.infer(context=context): + if isinstance(inferred, scoped_nodes.ClassDef): + if inferred.newstyle: + metaclass = inferred.metaclass(context=context) + if metaclass: + yield metaclass + continue + yield builtins.getattr("type")[0] + elif isinstance(inferred, (scoped_nodes.Lambda, bases.UnboundMethod)): + yield _function_type(inferred, builtins) + elif isinstance(inferred, scoped_nodes.Module): + yield _build_proxy_class("module", builtins) + else: + yield inferred._proxied + + +def object_type(node, context=None): + """Obtain the type of the given node + + This is used to implement the ``type`` builtin, which means that it's + used for inferring type calls, as well as used in a couple of other places + in the inference. + The node will be inferred first, so this function can support all + sorts of objects, as long as they support inference. + """ + + try: + types = set(_object_type(node, context)) + except exceptions.InferenceError: + return util.Uninferable + if len(types) > 1 or not types: + return util.Uninferable + return list(types)[0] + + +def _object_type_is_subclass(obj_type, class_or_seq, context=None): + if not isinstance(class_or_seq, (tuple, list)): + class_seq = (class_or_seq,) + else: + class_seq = class_or_seq + + if obj_type is util.Uninferable: + return util.Uninferable + + # Instances are not types + class_seq = [ + item if not isinstance(item, bases.Instance) else util.Uninferable + for item in class_seq + ] + # strict compatibility with issubclass + # issubclass(type, (object, 1)) evaluates to true + # issubclass(object, (1, type)) raises TypeError + for klass in class_seq: + if klass is util.Uninferable: + raise exceptions.AstroidTypeError("arg 2 must be a type or tuple of types") + + for obj_subclass in obj_type.mro(): + if obj_subclass == klass: + return True + return False + + +def object_isinstance(node, class_or_seq, context=None): + """Check if a node 'isinstance' any node in class_or_seq + + :param node: A given node + :param class_or_seq: Union[nodes.NodeNG, Sequence[nodes.NodeNG]] + :rtype: bool + + :raises AstroidTypeError: if the given ``classes_or_seq`` are not types + """ + obj_type = object_type(node, context) + if obj_type is util.Uninferable: + return util.Uninferable + return _object_type_is_subclass(obj_type, class_or_seq, context=context) + + +def object_issubclass(node, class_or_seq, context=None): + """Check if a type is a subclass of any node in class_or_seq + + :param node: A given node + :param class_or_seq: Union[Nodes.NodeNG, Sequence[nodes.NodeNG]] + :rtype: bool + + :raises AstroidTypeError: if the given ``classes_or_seq`` are not types + :raises AstroidError: if the type of the given node cannot be inferred + or its type's mro doesn't work + """ + if not isinstance(node, nodes.ClassDef): + raise TypeError("{node} needs to be a ClassDef node".format(node=node)) + return _object_type_is_subclass(node, class_or_seq, context=context) + + +def safe_infer(node, context=None): + """Return the inferred value for the given node. + + Return None if inference failed or if there is some ambiguity (more than + one node has been inferred). + """ + try: + inferit = node.infer(context=context) + value = next(inferit) + except exceptions.InferenceError: + return None + try: + next(inferit) + return None # None if there is ambiguity on the inferred node + except exceptions.InferenceError: + return None # there is some kind of ambiguity + except StopIteration: + return value + + +def has_known_bases(klass, context=None): + """Return true if all base classes of a class could be inferred.""" + try: + return klass._all_bases_known + except AttributeError: + pass + for base in klass.bases: + result = safe_infer(base, context=context) + # TODO: check for A->B->A->B pattern in class structure too? + if ( + not isinstance(result, scoped_nodes.ClassDef) + or result is klass + or not has_known_bases(result, context=context) + ): + klass._all_bases_known = False + return False + klass._all_bases_known = True + return True + + +def _type_check(type1, type2): + if not all(map(has_known_bases, (type1, type2))): + raise exceptions._NonDeducibleTypeHierarchy + + if not all([type1.newstyle, type2.newstyle]): + return False + try: + return type1 in type2.mro()[:-1] + except exceptions.MroError: + # The MRO is invalid. + raise exceptions._NonDeducibleTypeHierarchy + + +def is_subtype(type1, type2): + """Check if *type1* is a subtype of *type2*.""" + return _type_check(type1=type2, type2=type1) + + +def is_supertype(type1, type2): + """Check if *type2* is a supertype of *type1*.""" + return _type_check(type1, type2) + + +def class_instance_as_index(node): + """Get the value as an index for the given instance. + + If an instance provides an __index__ method, then it can + be used in some scenarios where an integer is expected, + for instance when multiplying or subscripting a list. + """ + context = contextmod.InferenceContext() + context.callcontext = contextmod.CallContext(args=[node]) + + try: + for inferred in node.igetattr("__index__", context=context): + if not isinstance(inferred, bases.BoundMethod): + continue + + for result in inferred.infer_call_result(node, context=context): + if isinstance(result, nodes.Const) and isinstance(result.value, int): + return result + except exceptions.InferenceError: + pass + return None + + +def object_len(node, context=None): + """Infer length of given node object + + :param Union[nodes.ClassDef, nodes.Instance] node: + :param node: Node to infer length of + + :raises AstroidTypeError: If an invalid node is returned + from __len__ method or no __len__ method exists + :raises InferenceError: If the given node cannot be inferred + or if multiple nodes are inferred + :rtype int: Integer length of node + """ + # pylint: disable=import-outside-toplevel; circular import + from astroid.objects import FrozenSet + + inferred_node = safe_infer(node, context=context) + if inferred_node is None or inferred_node is util.Uninferable: + raise exceptions.InferenceError(node=node) + if isinstance(inferred_node, nodes.Const) and isinstance( + inferred_node.value, (bytes, str) + ): + return len(inferred_node.value) + if isinstance(inferred_node, (nodes.List, nodes.Set, nodes.Tuple, FrozenSet)): + return len(inferred_node.elts) + if isinstance(inferred_node, nodes.Dict): + return len(inferred_node.items) + + node_type = object_type(inferred_node, context=context) + if not node_type: + raise exceptions.InferenceError(node=node) + + try: + len_call = next(node_type.igetattr("__len__", context=context)) + except exceptions.AttributeInferenceError: + raise exceptions.AstroidTypeError( + "object of type '{}' has no len()".format(node_type.pytype()) + ) + + result_of_len = next(len_call.infer_call_result(node, context)) + if ( + isinstance(result_of_len, nodes.Const) + and result_of_len.pytype() == "builtins.int" + ): + return result_of_len.value + if isinstance(result_of_len, bases.Instance) and result_of_len.is_subtype_of( + "builtins.int" + ): + # Fake a result as we don't know the arguments of the instance call. + return 0 + raise exceptions.AstroidTypeError( + "'{}' object cannot be interpreted as an integer".format(result_of_len) + ) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/inference.py b/Display/.venv/lib/python3.7/site-packages/astroid/inference.py new file mode 100644 index 0000000..bc3e1f9 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/inference.py @@ -0,0 +1,994 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2012 FELD Boris +# Copyright (c) 2013-2014 Google, Inc. +# Copyright (c) 2014-2020 Claudiu Popa +# Copyright (c) 2014 Eevee (Alex Munroe) +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2015 Dmitry Pribysh +# Copyright (c) 2016 Jakub Wilk +# Copyright (c) 2017 Michał Masłowski +# Copyright (c) 2017 Calen Pennington +# Copyright (c) 2017 Łukasz Rogalski +# Copyright (c) 2018-2019 Nick Drozd +# Copyright (c) 2018 Daniel Martin +# Copyright (c) 2018 Ville Skyttä +# Copyright (c) 2018 Bryce Guinta +# Copyright (c) 2018 Ashley Whetter +# Copyright (c) 2018 HoverHell +# Copyright (c) 2020 Leandro T. C. Melo + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""this module contains a set of functions to handle inference on astroid trees +""" + +import functools +import itertools +import operator + +import wrapt +from astroid import bases +from astroid import context as contextmod +from astroid import exceptions +from astroid import decorators +from astroid import helpers +from astroid import manager +from astroid import nodes +from astroid.interpreter import dunder_lookup +from astroid import protocols +from astroid import util + + +MANAGER = manager.AstroidManager() +# Prevents circular imports +objects = util.lazy_import("objects") + + +# .infer method ############################################################### + + +def infer_end(self, context=None): + """Inference's end for nodes that yield themselves on inference + + These are objects for which inference does not have any semantic, + such as Module or Consts. + """ + yield self + + +nodes.Module._infer = infer_end +nodes.ClassDef._infer = infer_end +nodes.Lambda._infer = infer_end +nodes.Const._infer = infer_end +nodes.Slice._infer = infer_end + + +def _infer_sequence_helper(node, context=None): + """Infer all values based on _BaseContainer.elts""" + values = [] + + for elt in node.elts: + if isinstance(elt, nodes.Starred): + starred = helpers.safe_infer(elt.value, context) + if not starred: + raise exceptions.InferenceError(node=node, context=context) + if not hasattr(starred, "elts"): + raise exceptions.InferenceError(node=node, context=context) + values.extend(_infer_sequence_helper(starred)) + elif isinstance(elt, nodes.NamedExpr): + value = helpers.safe_infer(elt.value, context) + if not value: + raise exceptions.InferenceError(node=node, context=context) + values.append(value) + else: + values.append(elt) + return values + + +@decorators.raise_if_nothing_inferred +def infer_sequence(self, context=None): + has_starred_named_expr = any( + isinstance(e, (nodes.Starred, nodes.NamedExpr)) for e in self.elts + ) + if has_starred_named_expr: + values = _infer_sequence_helper(self, context) + new_seq = type(self)( + lineno=self.lineno, col_offset=self.col_offset, parent=self.parent + ) + new_seq.postinit(values) + + yield new_seq + else: + yield self + + +nodes.List._infer = infer_sequence +nodes.Tuple._infer = infer_sequence +nodes.Set._infer = infer_sequence + + +def infer_map(self, context=None): + if not any(isinstance(k, nodes.DictUnpack) for k, _ in self.items): + yield self + else: + items = _infer_map(self, context) + new_seq = type(self)(self.lineno, self.col_offset, self.parent) + new_seq.postinit(list(items.items())) + yield new_seq + + +def _update_with_replacement(lhs_dict, rhs_dict): + """Delete nodes that equate to duplicate keys + + Since an astroid node doesn't 'equal' another node with the same value, + this function uses the as_string method to make sure duplicate keys + don't get through + + Note that both the key and the value are astroid nodes + + Fixes issue with DictUnpack causing duplicte keys + in inferred Dict items + + :param dict(nodes.NodeNG, nodes.NodeNG) lhs_dict: Dictionary to 'merge' nodes into + :param dict(nodes.NodeNG, nodes.NodeNG) rhs_dict: Dictionary with nodes to pull from + :return dict(nodes.NodeNG, nodes.NodeNG): merged dictionary of nodes + """ + combined_dict = itertools.chain(lhs_dict.items(), rhs_dict.items()) + # Overwrite keys which have the same string values + string_map = {key.as_string(): (key, value) for key, value in combined_dict} + # Return to dictionary + return dict(string_map.values()) + + +def _infer_map(node, context): + """Infer all values based on Dict.items""" + values = {} + for name, value in node.items: + if isinstance(name, nodes.DictUnpack): + double_starred = helpers.safe_infer(value, context) + if not double_starred: + raise exceptions.InferenceError + if not isinstance(double_starred, nodes.Dict): + raise exceptions.InferenceError(node=node, context=context) + unpack_items = _infer_map(double_starred, context) + values = _update_with_replacement(values, unpack_items) + else: + key = helpers.safe_infer(name, context=context) + value = helpers.safe_infer(value, context=context) + if any(not elem for elem in (key, value)): + raise exceptions.InferenceError(node=node, context=context) + values = _update_with_replacement(values, {key: value}) + return values + + +nodes.Dict._infer = infer_map + + +def _higher_function_scope(node): + """ Search for the first function which encloses the given + scope. This can be used for looking up in that function's + scope, in case looking up in a lower scope for a particular + name fails. + + :param node: A scope node. + :returns: + ``None``, if no parent function scope was found, + otherwise an instance of :class:`astroid.scoped_nodes.Function`, + which encloses the given node. + """ + current = node + while current.parent and not isinstance(current.parent, nodes.FunctionDef): + current = current.parent + if current and current.parent: + return current.parent + return None + + +def infer_name(self, context=None): + """infer a Name: use name lookup rules""" + frame, stmts = self.lookup(self.name) + if not stmts: + # Try to see if the name is enclosed in a nested function + # and use the higher (first function) scope for searching. + parent_function = _higher_function_scope(self.scope()) + if parent_function: + _, stmts = parent_function.lookup(self.name) + + if not stmts: + raise exceptions.NameInferenceError( + name=self.name, scope=self.scope(), context=context + ) + context = contextmod.copy_context(context) + context.lookupname = self.name + return bases._infer_stmts(stmts, context, frame) + + +# pylint: disable=no-value-for-parameter +nodes.Name._infer = decorators.raise_if_nothing_inferred( + decorators.path_wrapper(infer_name) +) +nodes.AssignName.infer_lhs = infer_name # won't work with a path wrapper + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def infer_call(self, context=None): + """infer a Call node by trying to guess what the function returns""" + callcontext = contextmod.copy_context(context) + callcontext.callcontext = contextmod.CallContext( + args=self.args, keywords=self.keywords + ) + callcontext.boundnode = None + if context is not None: + callcontext.extra_context = _populate_context_lookup(self, context.clone()) + + for callee in self.func.infer(context): + if callee is util.Uninferable: + yield callee + continue + try: + if hasattr(callee, "infer_call_result"): + yield from callee.infer_call_result(caller=self, context=callcontext) + except exceptions.InferenceError: + continue + return dict(node=self, context=context) + + +nodes.Call._infer = infer_call + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def infer_import(self, context=None, asname=True): + """infer an Import node: return the imported module/object""" + name = context.lookupname + if name is None: + raise exceptions.InferenceError(node=self, context=context) + + try: + if asname: + yield self.do_import_module(self.real_name(name)) + else: + yield self.do_import_module(name) + except exceptions.AstroidBuildingError as exc: + raise exceptions.InferenceError(node=self, context=context) from exc + + +nodes.Import._infer = infer_import + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def infer_import_from(self, context=None, asname=True): + """infer a ImportFrom node: return the imported module/object""" + name = context.lookupname + if name is None: + raise exceptions.InferenceError(node=self, context=context) + if asname: + name = self.real_name(name) + + try: + module = self.do_import_module() + except exceptions.AstroidBuildingError as exc: + raise exceptions.InferenceError(node=self, context=context) from exc + + try: + context = contextmod.copy_context(context) + context.lookupname = name + stmts = module.getattr(name, ignore_locals=module is self.root()) + return bases._infer_stmts(stmts, context) + except exceptions.AttributeInferenceError as error: + raise exceptions.InferenceError( + error.message, target=self, attribute=name, context=context + ) from error + + +nodes.ImportFrom._infer = infer_import_from + + +def infer_attribute(self, context=None): + """infer an Attribute node by using getattr on the associated object""" + for owner in self.expr.infer(context): + if owner is util.Uninferable: + yield owner + continue + + if context and context.boundnode: + # This handles the situation where the attribute is accessed through a subclass + # of a base class and the attribute is defined at the base class's level, + # by taking in consideration a redefinition in the subclass. + if isinstance(owner, bases.Instance) and isinstance( + context.boundnode, bases.Instance + ): + try: + if helpers.is_subtype( + helpers.object_type(context.boundnode), + helpers.object_type(owner), + ): + owner = context.boundnode + except exceptions._NonDeducibleTypeHierarchy: + # Can't determine anything useful. + pass + elif not context: + context = contextmod.InferenceContext() + + try: + context.boundnode = owner + yield from owner.igetattr(self.attrname, context) + except ( + exceptions.AttributeInferenceError, + exceptions.InferenceError, + AttributeError, + ): + pass + finally: + context.boundnode = None + return dict(node=self, context=context) + + +nodes.Attribute._infer = decorators.raise_if_nothing_inferred( + decorators.path_wrapper(infer_attribute) +) +# won't work with a path wrapper +nodes.AssignAttr.infer_lhs = decorators.raise_if_nothing_inferred(infer_attribute) + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def infer_global(self, context=None): + if context.lookupname is None: + raise exceptions.InferenceError(node=self, context=context) + try: + return bases._infer_stmts(self.root().getattr(context.lookupname), context) + except exceptions.AttributeInferenceError as error: + raise exceptions.InferenceError( + error.message, target=self, attribute=context.lookupname, context=context + ) from error + + +nodes.Global._infer = infer_global + + +_SUBSCRIPT_SENTINEL = object() + + +def infer_subscript(self, context=None): + """Inference for subscripts + + We're understanding if the index is a Const + or a slice, passing the result of inference + to the value's `getitem` method, which should + handle each supported index type accordingly. + """ + + found_one = False + for value in self.value.infer(context): + if value is util.Uninferable: + yield util.Uninferable + return None + for index in self.slice.infer(context): + if index is util.Uninferable: + yield util.Uninferable + return None + + # Try to deduce the index value. + index_value = _SUBSCRIPT_SENTINEL + if value.__class__ == bases.Instance: + index_value = index + elif index.__class__ == bases.Instance: + instance_as_index = helpers.class_instance_as_index(index) + if instance_as_index: + index_value = instance_as_index + else: + index_value = index + + if index_value is _SUBSCRIPT_SENTINEL: + raise exceptions.InferenceError(node=self, context=context) + + try: + assigned = value.getitem(index_value, context) + except ( + exceptions.AstroidTypeError, + exceptions.AstroidIndexError, + exceptions.AttributeInferenceError, + AttributeError, + ) as exc: + raise exceptions.InferenceError(node=self, context=context) from exc + + # Prevent inferring if the inferred subscript + # is the same as the original subscripted object. + if self is assigned or assigned is util.Uninferable: + yield util.Uninferable + return None + yield from assigned.infer(context) + found_one = True + + if found_one: + return dict(node=self, context=context) + return None + + +nodes.Subscript._infer = decorators.raise_if_nothing_inferred( + decorators.path_wrapper(infer_subscript) +) +nodes.Subscript.infer_lhs = decorators.raise_if_nothing_inferred(infer_subscript) + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def _infer_boolop(self, context=None): + """Infer a boolean operation (and / or / not). + + The function will calculate the boolean operation + for all pairs generated through inference for each component + node. + """ + values = self.values + if self.op == "or": + predicate = operator.truth + else: + predicate = operator.not_ + + try: + values = [value.infer(context=context) for value in values] + except exceptions.InferenceError: + yield util.Uninferable + return None + + for pair in itertools.product(*values): + if any(item is util.Uninferable for item in pair): + # Can't infer the final result, just yield Uninferable. + yield util.Uninferable + continue + + bool_values = [item.bool_value() for item in pair] + if any(item is util.Uninferable for item in bool_values): + # Can't infer the final result, just yield Uninferable. + yield util.Uninferable + continue + + # Since the boolean operations are short circuited operations, + # this code yields the first value for which the predicate is True + # and if no value respected the predicate, then the last value will + # be returned (or Uninferable if there was no last value). + # This is conforming to the semantics of `and` and `or`: + # 1 and 0 -> 1 + # 0 and 1 -> 0 + # 1 or 0 -> 1 + # 0 or 1 -> 1 + value = util.Uninferable + for value, bool_value in zip(pair, bool_values): + if predicate(bool_value): + yield value + break + else: + yield value + + return dict(node=self, context=context) + + +nodes.BoolOp._infer = _infer_boolop + + +# UnaryOp, BinOp and AugAssign inferences + + +def _filter_operation_errors(self, infer_callable, context, error): + for result in infer_callable(self, context): + if isinstance(result, error): + # For the sake of .infer(), we don't care about operation + # errors, which is the job of pylint. So return something + # which shows that we can't infer the result. + yield util.Uninferable + else: + yield result + + +def _infer_unaryop(self, context=None): + """Infer what an UnaryOp should return when evaluated.""" + for operand in self.operand.infer(context): + try: + yield operand.infer_unary_op(self.op) + except TypeError as exc: + # The operand doesn't support this operation. + yield util.BadUnaryOperationMessage(operand, self.op, exc) + except AttributeError as exc: + meth = protocols.UNARY_OP_METHOD[self.op] + if meth is None: + # `not node`. Determine node's boolean + # value and negate its result, unless it is + # Uninferable, which will be returned as is. + bool_value = operand.bool_value() + if bool_value is not util.Uninferable: + yield nodes.const_factory(not bool_value) + else: + yield util.Uninferable + else: + if not isinstance(operand, (bases.Instance, nodes.ClassDef)): + # The operation was used on something which + # doesn't support it. + yield util.BadUnaryOperationMessage(operand, self.op, exc) + continue + + try: + try: + methods = dunder_lookup.lookup(operand, meth) + except exceptions.AttributeInferenceError: + yield util.BadUnaryOperationMessage(operand, self.op, exc) + continue + + meth = methods[0] + inferred = next(meth.infer(context=context)) + if inferred is util.Uninferable or not inferred.callable(): + continue + + context = contextmod.copy_context(context) + context.callcontext = contextmod.CallContext(args=[operand]) + call_results = inferred.infer_call_result(self, context=context) + result = next(call_results, None) + if result is None: + # Failed to infer, return the same type. + yield operand + else: + yield result + except exceptions.AttributeInferenceError as exc: + # The unary operation special method was not found. + yield util.BadUnaryOperationMessage(operand, self.op, exc) + except exceptions.InferenceError: + yield util.Uninferable + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def infer_unaryop(self, context=None): + """Infer what an UnaryOp should return when evaluated.""" + yield from _filter_operation_errors( + self, _infer_unaryop, context, util.BadUnaryOperationMessage + ) + return dict(node=self, context=context) + + +nodes.UnaryOp._infer_unaryop = _infer_unaryop +nodes.UnaryOp._infer = infer_unaryop + + +def _is_not_implemented(const): + """Check if the given const node is NotImplemented.""" + return isinstance(const, nodes.Const) and const.value is NotImplemented + + +def _invoke_binop_inference(instance, opnode, op, other, context, method_name): + """Invoke binary operation inference on the given instance.""" + methods = dunder_lookup.lookup(instance, method_name) + context = contextmod.bind_context_to_node(context, instance) + method = methods[0] + inferred = next(method.infer(context=context)) + if inferred is util.Uninferable: + raise exceptions.InferenceError + return instance.infer_binary_op(opnode, op, other, context, inferred) + + +def _aug_op(instance, opnode, op, other, context, reverse=False): + """Get an inference callable for an augmented binary operation.""" + method_name = protocols.AUGMENTED_OP_METHOD[op] + return functools.partial( + _invoke_binop_inference, + instance=instance, + op=op, + opnode=opnode, + other=other, + context=context, + method_name=method_name, + ) + + +def _bin_op(instance, opnode, op, other, context, reverse=False): + """Get an inference callable for a normal binary operation. + + If *reverse* is True, then the reflected method will be used instead. + """ + if reverse: + method_name = protocols.REFLECTED_BIN_OP_METHOD[op] + else: + method_name = protocols.BIN_OP_METHOD[op] + return functools.partial( + _invoke_binop_inference, + instance=instance, + op=op, + opnode=opnode, + other=other, + context=context, + method_name=method_name, + ) + + +def _get_binop_contexts(context, left, right): + """Get contexts for binary operations. + + This will return two inference contexts, the first one + for x.__op__(y), the other one for y.__rop__(x), where + only the arguments are inversed. + """ + # The order is important, since the first one should be + # left.__op__(right). + for arg in (right, left): + new_context = context.clone() + new_context.callcontext = contextmod.CallContext(args=[arg]) + new_context.boundnode = None + yield new_context + + +def _same_type(type1, type2): + """Check if type1 is the same as type2.""" + return type1.qname() == type2.qname() + + +def _get_binop_flow( + left, left_type, binary_opnode, right, right_type, context, reverse_context +): + """Get the flow for binary operations. + + The rules are a bit messy: + + * if left and right have the same type, then only one + method will be called, left.__op__(right) + * if left and right are unrelated typewise, then first + left.__op__(right) is tried and if this does not exist + or returns NotImplemented, then right.__rop__(left) is tried. + * if left is a subtype of right, then only left.__op__(right) + is tried. + * if left is a supertype of right, then right.__rop__(left) + is first tried and then left.__op__(right) + """ + op = binary_opnode.op + if _same_type(left_type, right_type): + methods = [_bin_op(left, binary_opnode, op, right, context)] + elif helpers.is_subtype(left_type, right_type): + methods = [_bin_op(left, binary_opnode, op, right, context)] + elif helpers.is_supertype(left_type, right_type): + methods = [ + _bin_op(right, binary_opnode, op, left, reverse_context, reverse=True), + _bin_op(left, binary_opnode, op, right, context), + ] + else: + methods = [ + _bin_op(left, binary_opnode, op, right, context), + _bin_op(right, binary_opnode, op, left, reverse_context, reverse=True), + ] + return methods + + +def _get_aug_flow( + left, left_type, aug_opnode, right, right_type, context, reverse_context +): + """Get the flow for augmented binary operations. + + The rules are a bit messy: + + * if left and right have the same type, then left.__augop__(right) + is first tried and then left.__op__(right). + * if left and right are unrelated typewise, then + left.__augop__(right) is tried, then left.__op__(right) + is tried and then right.__rop__(left) is tried. + * if left is a subtype of right, then left.__augop__(right) + is tried and then left.__op__(right). + * if left is a supertype of right, then left.__augop__(right) + is tried, then right.__rop__(left) and then + left.__op__(right) + """ + bin_op = aug_opnode.op.strip("=") + aug_op = aug_opnode.op + if _same_type(left_type, right_type): + methods = [ + _aug_op(left, aug_opnode, aug_op, right, context), + _bin_op(left, aug_opnode, bin_op, right, context), + ] + elif helpers.is_subtype(left_type, right_type): + methods = [ + _aug_op(left, aug_opnode, aug_op, right, context), + _bin_op(left, aug_opnode, bin_op, right, context), + ] + elif helpers.is_supertype(left_type, right_type): + methods = [ + _aug_op(left, aug_opnode, aug_op, right, context), + _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True), + _bin_op(left, aug_opnode, bin_op, right, context), + ] + else: + methods = [ + _aug_op(left, aug_opnode, aug_op, right, context), + _bin_op(left, aug_opnode, bin_op, right, context), + _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True), + ] + return methods + + +def _infer_binary_operation(left, right, binary_opnode, context, flow_factory): + """Infer a binary operation between a left operand and a right operand + + This is used by both normal binary operations and augmented binary + operations, the only difference is the flow factory used. + """ + + context, reverse_context = _get_binop_contexts(context, left, right) + left_type = helpers.object_type(left) + right_type = helpers.object_type(right) + methods = flow_factory( + left, left_type, binary_opnode, right, right_type, context, reverse_context + ) + for method in methods: + try: + results = list(method()) + except AttributeError: + continue + except exceptions.AttributeInferenceError: + continue + except exceptions.InferenceError: + yield util.Uninferable + return + else: + if any(result is util.Uninferable for result in results): + yield util.Uninferable + return + + if all(map(_is_not_implemented, results)): + continue + not_implemented = sum( + 1 for result in results if _is_not_implemented(result) + ) + if not_implemented and not_implemented != len(results): + # Can't infer yet what this is. + yield util.Uninferable + return + + yield from results + return + # The operation doesn't seem to be supported so let the caller know about it + yield util.BadBinaryOperationMessage(left_type, binary_opnode.op, right_type) + + +def _infer_binop(self, context): + """Binary operation inference logic.""" + left = self.left + right = self.right + + # we use two separate contexts for evaluating lhs and rhs because + # 1. evaluating lhs may leave some undesired entries in context.path + # which may not let us infer right value of rhs + context = context or contextmod.InferenceContext() + lhs_context = contextmod.copy_context(context) + rhs_context = contextmod.copy_context(context) + lhs_iter = left.infer(context=lhs_context) + rhs_iter = right.infer(context=rhs_context) + for lhs, rhs in itertools.product(lhs_iter, rhs_iter): + if any(value is util.Uninferable for value in (rhs, lhs)): + # Don't know how to process this. + yield util.Uninferable + return + + try: + yield from _infer_binary_operation(lhs, rhs, self, context, _get_binop_flow) + except exceptions._NonDeducibleTypeHierarchy: + yield util.Uninferable + + +@decorators.yes_if_nothing_inferred +@decorators.path_wrapper +def infer_binop(self, context=None): + return _filter_operation_errors( + self, _infer_binop, context, util.BadBinaryOperationMessage + ) + + +nodes.BinOp._infer_binop = _infer_binop +nodes.BinOp._infer = infer_binop + + +def _infer_augassign(self, context=None): + """Inference logic for augmented binary operations.""" + if context is None: + context = contextmod.InferenceContext() + + rhs_context = context.clone() + + lhs_iter = self.target.infer_lhs(context=context) + rhs_iter = self.value.infer(context=rhs_context) + for lhs, rhs in itertools.product(lhs_iter, rhs_iter): + if any(value is util.Uninferable for value in (rhs, lhs)): + # Don't know how to process this. + yield util.Uninferable + return + + try: + yield from _infer_binary_operation( + left=lhs, + right=rhs, + binary_opnode=self, + context=context, + flow_factory=_get_aug_flow, + ) + except exceptions._NonDeducibleTypeHierarchy: + yield util.Uninferable + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def infer_augassign(self, context=None): + return _filter_operation_errors( + self, _infer_augassign, context, util.BadBinaryOperationMessage + ) + + +nodes.AugAssign._infer_augassign = _infer_augassign +nodes.AugAssign._infer = infer_augassign + +# End of binary operation inference. + + +@decorators.raise_if_nothing_inferred +def infer_arguments(self, context=None): + name = context.lookupname + if name is None: + raise exceptions.InferenceError(node=self, context=context) + return protocols._arguments_infer_argname(self, name, context) + + +nodes.Arguments._infer = infer_arguments + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def infer_assign(self, context=None): + """infer a AssignName/AssignAttr: need to inspect the RHS part of the + assign node + """ + if isinstance(self.parent, nodes.AugAssign): + return self.parent.infer(context) + + stmts = list(self.assigned_stmts(context=context)) + return bases._infer_stmts(stmts, context) + + +nodes.AssignName._infer = infer_assign +nodes.AssignAttr._infer = infer_assign + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def infer_empty_node(self, context=None): + if not self.has_underlying_object(): + yield util.Uninferable + else: + try: + yield from MANAGER.infer_ast_from_something(self.object, context=context) + except exceptions.AstroidError: + yield util.Uninferable + + +nodes.EmptyNode._infer = infer_empty_node + + +@decorators.raise_if_nothing_inferred +def infer_index(self, context=None): + return self.value.infer(context) + + +nodes.Index._infer = infer_index + +# TODO: move directly into bases.Instance when the dependency hell +# will be solved. +def instance_getitem(self, index, context=None): + # Rewrap index to Const for this case + new_context = contextmod.bind_context_to_node(context, self) + if not context: + context = new_context + + # Create a new callcontext for providing index as an argument. + new_context.callcontext = contextmod.CallContext(args=[index]) + + method = next(self.igetattr("__getitem__", context=context), None) + if not isinstance(method, bases.BoundMethod): + raise exceptions.InferenceError( + "Could not find __getitem__ for {node!r}.", node=self, context=context + ) + + return next(method.infer_call_result(self, new_context)) + + +bases.Instance.getitem = instance_getitem + + +def _populate_context_lookup(call, context): + # Allows context to be saved for later + # for inference inside a function + context_lookup = {} + if context is None: + return context_lookup + for arg in call.args: + if isinstance(arg, nodes.Starred): + context_lookup[arg.value] = context + else: + context_lookup[arg] = context + keywords = call.keywords if call.keywords is not None else [] + for keyword in keywords: + context_lookup[keyword.value] = context + return context_lookup + + +@decorators.raise_if_nothing_inferred +def infer_ifexp(self, context=None): + """Support IfExp inference + + If we can't infer the truthiness of the condition, we default + to inferring both branches. Otherwise, we infer either branch + depending on the condition. + """ + both_branches = False + # We use two separate contexts for evaluating lhs and rhs because + # evaluating lhs may leave some undesired entries in context.path + # which may not let us infer right value of rhs. + + context = context or contextmod.InferenceContext() + lhs_context = contextmod.copy_context(context) + rhs_context = contextmod.copy_context(context) + try: + test = next(self.test.infer(context=context.clone())) + except exceptions.InferenceError: + both_branches = True + else: + if test is not util.Uninferable: + if test.bool_value(): + yield from self.body.infer(context=lhs_context) + else: + yield from self.orelse.infer(context=rhs_context) + else: + both_branches = True + if both_branches: + yield from self.body.infer(context=lhs_context) + yield from self.orelse.infer(context=rhs_context) + + +nodes.IfExp._infer = infer_ifexp + + +# pylint: disable=dangerous-default-value +@wrapt.decorator +def _cached_generator(func, instance, args, kwargs, _cache={}): + node = args[0] + try: + return iter(_cache[func, id(node)]) + except KeyError: + result = func(*args, **kwargs) + # Need to keep an iterator around + original, copy = itertools.tee(result) + _cache[func, id(node)] = list(copy) + return original + + +# When inferring a property, we instantiate a new `objects.Property` object, +# which in turn, because it inherits from `FunctionDef`, sets itself in the locals +# of the wrapping frame. This means that everytime we infer a property, the locals +# are mutated with a new instance of the property. This is why we cache the result +# of the function's inference. +@_cached_generator +def infer_functiondef(self, context=None): + if not self.decorators or not bases._is_property(self): + yield self + return dict(node=self, context=context) + + prop_func = objects.Property( + function=self, + name=self.name, + doc=self.doc, + lineno=self.lineno, + parent=self.parent, + col_offset=self.col_offset, + ) + prop_func.postinit(body=[], args=self.args) + yield prop_func + return dict(node=self, context=context) + + +nodes.FunctionDef._infer = infer_functiondef diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/interpreter/__init__.py b/Display/.venv/lib/python3.7/site-packages/astroid/interpreter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/interpreter/_import/__init__.py b/Display/.venv/lib/python3.7/site-packages/astroid/interpreter/_import/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/interpreter/_import/spec.py b/Display/.venv/lib/python3.7/site-packages/astroid/interpreter/_import/spec.py new file mode 100644 index 0000000..3cf5fea --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/interpreter/_import/spec.py @@ -0,0 +1,346 @@ +# Copyright (c) 2016-2018 Claudiu Popa +# Copyright (c) 2016 Derek Gustafson +# Copyright (c) 2017 Chris Philip +# Copyright (c) 2017 Hugo +# Copyright (c) 2017 ioanatia +# Copyright (c) 2017 Calen Pennington +# Copyright (c) 2018 Nick Drozd +# Copyright (c) 2019 Hugo van Kemenade +# Copyright (c) 2019 Ashley Whetter + +import abc +import collections +import distutils +import enum +import imp +import os +import sys +import zipimport + +try: + import importlib.machinery + + _HAS_MACHINERY = True +except ImportError: + _HAS_MACHINERY = False + +try: + from functools import lru_cache +except ImportError: + from backports.functools_lru_cache import lru_cache + +from . import util + +ModuleType = enum.Enum( + "ModuleType", + "C_BUILTIN C_EXTENSION PKG_DIRECTORY " + "PY_CODERESOURCE PY_COMPILED PY_FROZEN PY_RESOURCE " + "PY_SOURCE PY_ZIPMODULE PY_NAMESPACE", +) +_ImpTypes = { + imp.C_BUILTIN: ModuleType.C_BUILTIN, + imp.C_EXTENSION: ModuleType.C_EXTENSION, + imp.PKG_DIRECTORY: ModuleType.PKG_DIRECTORY, + imp.PY_COMPILED: ModuleType.PY_COMPILED, + imp.PY_FROZEN: ModuleType.PY_FROZEN, + imp.PY_SOURCE: ModuleType.PY_SOURCE, +} +if hasattr(imp, "PY_RESOURCE"): + _ImpTypes[imp.PY_RESOURCE] = ModuleType.PY_RESOURCE +if hasattr(imp, "PY_CODERESOURCE"): + _ImpTypes[imp.PY_CODERESOURCE] = ModuleType.PY_CODERESOURCE + + +def _imp_type_to_module_type(imp_type): + return _ImpTypes[imp_type] + + +_ModuleSpec = collections.namedtuple( + "_ModuleSpec", "name type location " "origin submodule_search_locations" +) + + +class ModuleSpec(_ModuleSpec): + """Defines a class similar to PEP 420's ModuleSpec + + A module spec defines a name of a module, its type, location + and where submodules can be found, if the module is a package. + """ + + def __new__( + cls, + name, + module_type, + location=None, + origin=None, + submodule_search_locations=None, + ): + return _ModuleSpec.__new__( + cls, + name=name, + type=module_type, + location=location, + origin=origin, + submodule_search_locations=submodule_search_locations, + ) + + +class Finder: + """A finder is a class which knows how to find a particular module.""" + + def __init__(self, path=None): + self._path = path or sys.path + + @abc.abstractmethod + def find_module(self, modname, module_parts, processed, submodule_path): + """Find the given module + + Each finder is responsible for each protocol of finding, as long as + they all return a ModuleSpec. + + :param str modname: The module which needs to be searched. + :param list module_parts: It should be a list of strings, + where each part contributes to the module's + namespace. + :param list processed: What parts from the module parts were processed + so far. + :param list submodule_path: A list of paths where the module + can be looked into. + :returns: A ModuleSpec, describing how and where the module was found, + None, otherwise. + """ + + def contribute_to_path(self, spec, processed): + """Get a list of extra paths where this finder can search.""" + + +class ImpFinder(Finder): + """A finder based on the imp module.""" + + def find_module(self, modname, module_parts, processed, submodule_path): + if submodule_path is not None: + submodule_path = list(submodule_path) + try: + stream, mp_filename, mp_desc = imp.find_module(modname, submodule_path) + except ImportError: + return None + + # Close resources. + if stream: + stream.close() + + return ModuleSpec( + name=modname, + location=mp_filename, + module_type=_imp_type_to_module_type(mp_desc[2]), + ) + + def contribute_to_path(self, spec, processed): + if spec.location is None: + # Builtin. + return None + + if _is_setuptools_namespace(spec.location): + # extend_path is called, search sys.path for module/packages + # of this name see pkgutil.extend_path documentation + path = [ + os.path.join(p, *processed) + for p in sys.path + if os.path.isdir(os.path.join(p, *processed)) + ] + # We already import distutils elsewhere in astroid, + # so if it is the same module, we can use it directly. + elif spec.name == "distutils" and spec.location in distutils.__path__: + # distutils is patched inside virtualenvs to pick up submodules + # from the original Python, not from the virtualenv itself. + path = list(distutils.__path__) + else: + path = [spec.location] + return path + + +class ExplicitNamespacePackageFinder(ImpFinder): + """A finder for the explicit namespace packages, generated through pkg_resources.""" + + def find_module(self, modname, module_parts, processed, submodule_path): + if processed: + modname = ".".join(processed + [modname]) + if util.is_namespace(modname) and modname in sys.modules: + submodule_path = sys.modules[modname].__path__ + return ModuleSpec( + name=modname, + location="", + origin="namespace", + module_type=ModuleType.PY_NAMESPACE, + submodule_search_locations=submodule_path, + ) + return None + + def contribute_to_path(self, spec, processed): + return spec.submodule_search_locations + + +class ZipFinder(Finder): + """Finder that knows how to find a module inside zip files.""" + + def __init__(self, path): + super().__init__(path) + self._zipimporters = _precache_zipimporters(path) + + def find_module(self, modname, module_parts, processed, submodule_path): + try: + file_type, filename, path = _search_zip(module_parts, self._zipimporters) + except ImportError: + return None + + return ModuleSpec( + name=modname, + location=filename, + origin="egg", + module_type=file_type, + submodule_search_locations=path, + ) + + +class PathSpecFinder(Finder): + """Finder based on importlib.machinery.PathFinder.""" + + def find_module(self, modname, module_parts, processed, submodule_path): + spec = importlib.machinery.PathFinder.find_spec(modname, path=submodule_path) + if spec: + # origin can be either a string on older Python versions + # or None in case it is a namespace package: + # https://github.com/python/cpython/pull/5481 + is_namespace_pkg = spec.origin in ("namespace", None) + location = spec.origin if not is_namespace_pkg else None + module_type = ModuleType.PY_NAMESPACE if is_namespace_pkg else None + spec = ModuleSpec( + name=spec.name, + location=location, + origin=spec.origin, + module_type=module_type, + submodule_search_locations=list(spec.submodule_search_locations or []), + ) + return spec + + def contribute_to_path(self, spec, processed): + if spec.type == ModuleType.PY_NAMESPACE: + return spec.submodule_search_locations + return None + + +_SPEC_FINDERS = (ImpFinder, ZipFinder) +if _HAS_MACHINERY: + _SPEC_FINDERS += (PathSpecFinder,) +_SPEC_FINDERS += (ExplicitNamespacePackageFinder,) + + +def _is_setuptools_namespace(location): + try: + with open(os.path.join(location, "__init__.py"), "rb") as stream: + data = stream.read(4096) + except IOError: + pass + else: + extend_path = b"pkgutil" in data and b"extend_path" in data + declare_namespace = ( + b"pkg_resources" in data and b"declare_namespace(__name__)" in data + ) + return extend_path or declare_namespace + + +@lru_cache() +def _cached_set_diff(left, right): + result = set(left) + result.difference_update(right) + return result + + +def _precache_zipimporters(path=None): + pic = sys.path_importer_cache + + # When measured, despite having the same complexity (O(n)), + # converting to tuples and then caching the conversion to sets + # and the set difference is faster than converting to sets + # and then only caching the set difference. + + req_paths = tuple(path or sys.path) + cached_paths = tuple(pic) + new_paths = _cached_set_diff(req_paths, cached_paths) + for entry_path in new_paths: + try: + pic[entry_path] = zipimport.zipimporter(entry_path) + except zipimport.ZipImportError: + continue + return pic + + +def _search_zip(modpath, pic): + for filepath, importer in list(pic.items()): + if importer is not None: + found = importer.find_module(modpath[0]) + if found: + if not importer.find_module(os.path.sep.join(modpath)): + raise ImportError( + "No module named %s in %s/%s" + % (".".join(modpath[1:]), filepath, modpath) + ) + # import code; code.interact(local=locals()) + return ( + ModuleType.PY_ZIPMODULE, + os.path.abspath(filepath) + os.path.sep + os.path.sep.join(modpath), + filepath, + ) + raise ImportError("No module named %s" % ".".join(modpath)) + + +def _find_spec_with_path(search_path, modname, module_parts, processed, submodule_path): + finders = [finder(search_path) for finder in _SPEC_FINDERS] + for finder in finders: + spec = finder.find_module(modname, module_parts, processed, submodule_path) + if spec is None: + continue + return finder, spec + + raise ImportError("No module named %s" % ".".join(module_parts)) + + +def find_spec(modpath, path=None): + """Find a spec for the given module. + + :type modpath: list or tuple + :param modpath: + split module's name (i.e name of a module or package split + on '.'), with leading empty strings for explicit relative import + + :type path: list or None + :param path: + optional list of path where the module or package should be + searched (use sys.path if nothing or None is given) + + :rtype: ModuleSpec + :return: A module spec, which describes how the module was + found and where. + """ + _path = path or sys.path + + # Need a copy for not mutating the argument. + modpath = modpath[:] + + submodule_path = None + module_parts = modpath[:] + processed = [] + + while modpath: + modname = modpath.pop(0) + finder, spec = _find_spec_with_path( + _path, modname, module_parts, processed, submodule_path or path + ) + processed.append(modname) + if modpath: + submodule_path = finder.contribute_to_path(spec, processed) + + if spec.type == ModuleType.PKG_DIRECTORY: + spec = spec._replace(submodule_search_locations=submodule_path) + + return spec diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/interpreter/_import/util.py b/Display/.venv/lib/python3.7/site-packages/astroid/interpreter/_import/util.py new file mode 100644 index 0000000..a917bd3 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/interpreter/_import/util.py @@ -0,0 +1,10 @@ +# Copyright (c) 2016, 2018 Claudiu Popa + +try: + import pkg_resources +except ImportError: + pkg_resources = None + + +def is_namespace(modname): + return pkg_resources is not None and modname in pkg_resources._namespace_packages diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/interpreter/dunder_lookup.py b/Display/.venv/lib/python3.7/site-packages/astroid/interpreter/dunder_lookup.py new file mode 100644 index 0000000..0ae9bc9 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/interpreter/dunder_lookup.py @@ -0,0 +1,66 @@ +# Copyright (c) 2016-2018 Claudiu Popa +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""Contains logic for retrieving special methods. + +This implementation does not rely on the dot attribute access +logic, found in ``.getattr()``. The difference between these two +is that the dunder methods are looked with the type slots +(you can find more about these here +http://lucumr.pocoo.org/2014/8/16/the-python-i-would-like-to-see/) +As such, the lookup for the special methods is actually simpler than +the dot attribute access. +""" +import itertools + +import astroid +from astroid import exceptions + + +def _lookup_in_mro(node, name): + attrs = node.locals.get(name, []) + + nodes = itertools.chain.from_iterable( + ancestor.locals.get(name, []) for ancestor in node.ancestors(recurs=True) + ) + values = list(itertools.chain(attrs, nodes)) + if not values: + raise exceptions.AttributeInferenceError(attribute=name, target=node) + + return values + + +def lookup(node, name): + """Lookup the given special method name in the given *node* + + If the special method was found, then a list of attributes + will be returned. Otherwise, `astroid.AttributeInferenceError` + is going to be raised. + """ + if isinstance( + node, (astroid.List, astroid.Tuple, astroid.Const, astroid.Dict, astroid.Set) + ): + return _builtin_lookup(node, name) + if isinstance(node, astroid.Instance): + return _lookup_in_mro(node, name) + if isinstance(node, astroid.ClassDef): + return _class_lookup(node, name) + + raise exceptions.AttributeInferenceError(attribute=name, target=node) + + +def _class_lookup(node, name): + metaclass = node.metaclass() + if metaclass is None: + raise exceptions.AttributeInferenceError(attribute=name, target=node) + + return _lookup_in_mro(metaclass, name) + + +def _builtin_lookup(node, name): + values = node.locals.get(name, []) + if not values: + raise exceptions.AttributeInferenceError(attribute=name, target=node) + + return values diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/interpreter/objectmodel.py b/Display/.venv/lib/python3.7/site-packages/astroid/interpreter/objectmodel.py new file mode 100644 index 0000000..277c825 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/interpreter/objectmodel.py @@ -0,0 +1,801 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2016-2019 Claudiu Popa +# Copyright (c) 2016 Derek Gustafson +# Copyright (c) 2017-2018 Bryce Guinta +# Copyright (c) 2017 Ceridwen +# Copyright (c) 2017 Calen Pennington +# Copyright (c) 2018 Ville Skyttä +# Copyright (c) 2018 Nick Drozd +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER +""" +Data object model, as per https://docs.python.org/3/reference/datamodel.html. + +This module describes, at least partially, a data object model for some +of astroid's nodes. The model contains special attributes that nodes such +as functions, classes, modules etc have, such as __doc__, __class__, +__module__ etc, being used when doing attribute lookups over nodes. + +For instance, inferring `obj.__class__` will first trigger an inference +of the `obj` variable. If it was successfully inferred, then an attribute +`__class__ will be looked for in the inferred object. This is the part +where the data model occurs. The model is attached to those nodes +and the lookup mechanism will try to see if attributes such as +`__class__` are defined by the model or not. If they are defined, +the model will be requested to return the corresponding value of that +attribute. Thus the model can be viewed as a special part of the lookup +mechanism. +""" + +import itertools +import pprint +import os +import types +from functools import lru_cache + +import astroid +from astroid import context as contextmod +from astroid import exceptions +from astroid import node_classes + + +IMPL_PREFIX = "attr_" + + +def _dunder_dict(instance, attributes): + obj = node_classes.Dict(parent=instance) + + # Convert the keys to node strings + keys = [ + node_classes.Const(value=value, parent=obj) for value in list(attributes.keys()) + ] + + # The original attribute has a list of elements for each key, + # but that is not useful for retrieving the special attribute's value. + # In this case, we're picking the last value from each list. + values = [elem[-1] for elem in attributes.values()] + + obj.postinit(list(zip(keys, values))) + return obj + + +class ObjectModel: + def __init__(self): + self._instance = None + + def __repr__(self): + result = [] + cname = type(self).__name__ + string = "%(cname)s(%(fields)s)" + alignment = len(cname) + 1 + for field in sorted(self.attributes()): + width = 80 - len(field) - alignment + lines = pprint.pformat(field, indent=2, width=width).splitlines(True) + + inner = [lines[0]] + for line in lines[1:]: + inner.append(" " * alignment + line) + result.append(field) + + return string % { + "cname": cname, + "fields": (",\n" + " " * alignment).join(result), + } + + def __call__(self, instance): + self._instance = instance + return self + + def __get__(self, instance, cls=None): + # ObjectModel needs to be a descriptor so that just doing + # `special_attributes = SomeObjectModel` should be enough in the body of a node. + # But at the same time, node.special_attributes should return an object + # which can be used for manipulating the special attributes. That's the reason + # we pass the instance through which it got accessed to ObjectModel.__call__, + # returning itself afterwards, so we can still have access to the + # underlying data model and to the instance for which it got accessed. + return self(instance) + + def __contains__(self, name): + return name in self.attributes() + + @lru_cache(maxsize=None) + def attributes(self): + """Get the attributes which are exported by this object model.""" + return [ + obj[len(IMPL_PREFIX) :] for obj in dir(self) if obj.startswith(IMPL_PREFIX) + ] + + def lookup(self, name): + """Look up the given *name* in the current model + + It should return an AST or an interpreter object, + but if the name is not found, then an AttributeInferenceError will be raised. + """ + + if name in self.attributes(): + return getattr(self, IMPL_PREFIX + name) + raise exceptions.AttributeInferenceError(target=self._instance, attribute=name) + + +class ModuleModel(ObjectModel): + def _builtins(self): + builtins_ast_module = astroid.MANAGER.builtins_module + return builtins_ast_module.special_attributes.lookup("__dict__") + + @property + def attr_builtins(self): + return self._builtins() + + @property + def attr___path__(self): + if not self._instance.package: + raise exceptions.AttributeInferenceError( + target=self._instance, attribute="__path__" + ) + + path_objs = [ + node_classes.Const( + value=path + if not path.endswith("__init__.py") + else os.path.dirname(path), + parent=self._instance, + ) + for path in self._instance.path + ] + + container = node_classes.List(parent=self._instance) + container.postinit(path_objs) + + return container + + @property + def attr___name__(self): + return node_classes.Const(value=self._instance.name, parent=self._instance) + + @property + def attr___doc__(self): + return node_classes.Const(value=self._instance.doc, parent=self._instance) + + @property + def attr___file__(self): + return node_classes.Const(value=self._instance.file, parent=self._instance) + + @property + def attr___dict__(self): + return _dunder_dict(self._instance, self._instance.globals) + + @property + def attr___package__(self): + if not self._instance.package: + value = "" + else: + value = self._instance.name + + return node_classes.Const(value=value, parent=self._instance) + + # These are related to the Python 3 implementation of the + # import system, + # https://docs.python.org/3/reference/import.html#import-related-module-attributes + + @property + def attr___spec__(self): + # No handling for now. + return node_classes.Unknown() + + @property + def attr___loader__(self): + # No handling for now. + return node_classes.Unknown() + + @property + def attr___cached__(self): + # No handling for now. + return node_classes.Unknown() + + +class FunctionModel(ObjectModel): + @property + def attr___name__(self): + return node_classes.Const(value=self._instance.name, parent=self._instance) + + @property + def attr___doc__(self): + return node_classes.Const(value=self._instance.doc, parent=self._instance) + + @property + def attr___qualname__(self): + return node_classes.Const(value=self._instance.qname(), parent=self._instance) + + @property + def attr___defaults__(self): + func = self._instance + if not func.args.defaults: + return node_classes.Const(value=None, parent=func) + + defaults_obj = node_classes.Tuple(parent=func) + defaults_obj.postinit(func.args.defaults) + return defaults_obj + + @property + def attr___annotations__(self): + obj = node_classes.Dict(parent=self._instance) + + if not self._instance.returns: + returns = None + else: + returns = self._instance.returns + + args = self._instance.args + pair_annotations = itertools.chain( + zip(args.args or [], args.annotations), + zip(args.kwonlyargs, args.kwonlyargs_annotations), + zip(args.posonlyargs or [], args.posonlyargs_annotations), + ) + + annotations = { + arg.name: annotation for (arg, annotation) in pair_annotations if annotation + } + if args.varargannotation: + annotations[args.vararg] = args.varargannotation + if args.kwargannotation: + annotations[args.kwarg] = args.kwargannotation + if returns: + annotations["return"] = returns + + items = [ + (node_classes.Const(key, parent=obj), value) + for (key, value) in annotations.items() + ] + + obj.postinit(items) + return obj + + @property + def attr___dict__(self): + return node_classes.Dict(parent=self._instance) + + attr___globals__ = attr___dict__ + + @property + def attr___kwdefaults__(self): + def _default_args(args, parent): + for arg in args.kwonlyargs: + try: + default = args.default_value(arg.name) + except exceptions.NoDefault: + continue + + name = node_classes.Const(arg.name, parent=parent) + yield name, default + + args = self._instance.args + obj = node_classes.Dict(parent=self._instance) + defaults = dict(_default_args(args, obj)) + + obj.postinit(list(defaults.items())) + return obj + + @property + def attr___module__(self): + return node_classes.Const(self._instance.root().qname()) + + @property + def attr___get__(self): + # pylint: disable=import-outside-toplevel; circular import + from astroid import bases + + func = self._instance + + class DescriptorBoundMethod(bases.BoundMethod): + """Bound method which knows how to understand calling descriptor binding.""" + + def implicit_parameters(self): + # Different than BoundMethod since the signature + # is different. + return 0 + + def infer_call_result(self, caller, context=None): + if len(caller.args) > 2 or len(caller.args) < 1: + raise exceptions.InferenceError( + "Invalid arguments for descriptor binding", + target=self, + context=context, + ) + + context = contextmod.copy_context(context) + cls = next(caller.args[0].infer(context=context)) + + if cls is astroid.Uninferable: + raise exceptions.InferenceError( + "Invalid class inferred", target=self, context=context + ) + + # For some reason func is a Node that the below + # code is not expecting + if isinstance(func, bases.BoundMethod): + yield func + return + + # Rebuild the original value, but with the parent set as the + # class where it will be bound. + new_func = func.__class__( + name=func.name, + doc=func.doc, + lineno=func.lineno, + col_offset=func.col_offset, + ) + # pylint: disable=no-member + new_func.postinit(func.args, func.body, func.decorators, func.returns) + + # Build a proper bound method that points to our newly built function. + proxy = bases.UnboundMethod(new_func) + yield bases.BoundMethod(proxy=proxy, bound=cls) + + @property + def args(self): + """Overwrite the underlying args to match those of the underlying func + + Usually the underlying *func* is a function/method, as in: + + def test(self): + pass + + This has only the *self* parameter but when we access test.__get__ + we get a new object which has two parameters, *self* and *type*. + """ + nonlocal func + positional_or_keyword_params = func.args.args.copy() + positional_or_keyword_params.append(astroid.AssignName(name="type")) + + positional_only_params = func.args.posonlyargs.copy() + + arguments = astroid.Arguments(parent=func.args.parent) + arguments.postinit( + args=positional_or_keyword_params, + posonlyargs=positional_only_params, + defaults=[], + kwonlyargs=[], + kw_defaults=[], + annotations=[], + ) + return arguments + + return DescriptorBoundMethod(proxy=self._instance, bound=self._instance) + + # These are here just for completion. + @property + def attr___ne__(self): + return node_classes.Unknown() + + attr___subclasshook__ = attr___ne__ + attr___str__ = attr___ne__ + attr___sizeof__ = attr___ne__ + attr___setattr___ = attr___ne__ + attr___repr__ = attr___ne__ + attr___reduce__ = attr___ne__ + attr___reduce_ex__ = attr___ne__ + attr___new__ = attr___ne__ + attr___lt__ = attr___ne__ + attr___eq__ = attr___ne__ + attr___gt__ = attr___ne__ + attr___format__ = attr___ne__ + attr___delattr___ = attr___ne__ + attr___getattribute__ = attr___ne__ + attr___hash__ = attr___ne__ + attr___init__ = attr___ne__ + attr___dir__ = attr___ne__ + attr___call__ = attr___ne__ + attr___class__ = attr___ne__ + attr___closure__ = attr___ne__ + attr___code__ = attr___ne__ + + +class ClassModel(ObjectModel): + @property + def attr___module__(self): + return node_classes.Const(self._instance.root().qname()) + + @property + def attr___name__(self): + return node_classes.Const(self._instance.name) + + @property + def attr___qualname__(self): + return node_classes.Const(self._instance.qname()) + + @property + def attr___doc__(self): + return node_classes.Const(self._instance.doc) + + @property + def attr___mro__(self): + if not self._instance.newstyle: + raise exceptions.AttributeInferenceError( + target=self._instance, attribute="__mro__" + ) + + mro = self._instance.mro() + obj = node_classes.Tuple(parent=self._instance) + obj.postinit(mro) + return obj + + @property + def attr_mro(self): + if not self._instance.newstyle: + raise exceptions.AttributeInferenceError( + target=self._instance, attribute="mro" + ) + + # pylint: disable=import-outside-toplevel; circular import + from astroid import bases + + other_self = self + + # Cls.mro is a method and we need to return one in order to have a proper inference. + # The method we're returning is capable of inferring the underlying MRO though. + class MroBoundMethod(bases.BoundMethod): + def infer_call_result(self, caller, context=None): + yield other_self.attr___mro__ + + implicit_metaclass = self._instance.implicit_metaclass() + mro_method = implicit_metaclass.locals["mro"][0] + return MroBoundMethod(proxy=mro_method, bound=implicit_metaclass) + + @property + def attr___bases__(self): + obj = node_classes.Tuple() + context = contextmod.InferenceContext() + elts = list(self._instance._inferred_bases(context)) + obj.postinit(elts=elts) + return obj + + @property + def attr___class__(self): + # pylint: disable=import-outside-toplevel; circular import + from astroid import helpers + + return helpers.object_type(self._instance) + + @property + def attr___subclasses__(self): + """Get the subclasses of the underlying class + + This looks only in the current module for retrieving the subclasses, + thus it might miss a couple of them. + """ + # pylint: disable=import-outside-toplevel; circular import + from astroid import bases + from astroid import scoped_nodes + + if not self._instance.newstyle: + raise exceptions.AttributeInferenceError( + target=self._instance, attribute="__subclasses__" + ) + + qname = self._instance.qname() + root = self._instance.root() + classes = [ + cls + for cls in root.nodes_of_class(scoped_nodes.ClassDef) + if cls != self._instance and cls.is_subtype_of(qname) + ] + + obj = node_classes.List(parent=self._instance) + obj.postinit(classes) + + class SubclassesBoundMethod(bases.BoundMethod): + def infer_call_result(self, caller, context=None): + yield obj + + implicit_metaclass = self._instance.implicit_metaclass() + subclasses_method = implicit_metaclass.locals["__subclasses__"][0] + return SubclassesBoundMethod(proxy=subclasses_method, bound=implicit_metaclass) + + @property + def attr___dict__(self): + return node_classes.Dict(parent=self._instance) + + +class SuperModel(ObjectModel): + @property + def attr___thisclass__(self): + return self._instance.mro_pointer + + @property + def attr___self_class__(self): + return self._instance._self_class + + @property + def attr___self__(self): + return self._instance.type + + @property + def attr___class__(self): + return self._instance._proxied + + +class UnboundMethodModel(ObjectModel): + @property + def attr___class__(self): + # pylint: disable=import-outside-toplevel; circular import + from astroid import helpers + + return helpers.object_type(self._instance) + + @property + def attr___func__(self): + return self._instance._proxied + + @property + def attr___self__(self): + return node_classes.Const(value=None, parent=self._instance) + + attr_im_func = attr___func__ + attr_im_class = attr___class__ + attr_im_self = attr___self__ + + +class BoundMethodModel(FunctionModel): + @property + def attr___func__(self): + return self._instance._proxied._proxied + + @property + def attr___self__(self): + return self._instance.bound + + +class GeneratorModel(FunctionModel): + def __new__(cls, *args, **kwargs): + # Append the values from the GeneratorType unto this object. + ret = super(GeneratorModel, cls).__new__(cls, *args, **kwargs) + generator = astroid.MANAGER.builtins_module["generator"] + for name, values in generator.locals.items(): + method = values[0] + patched = lambda cls, meth=method: meth + + setattr(type(ret), IMPL_PREFIX + name, property(patched)) + + return ret + + @property + def attr___name__(self): + return node_classes.Const( + value=self._instance.parent.name, parent=self._instance + ) + + @property + def attr___doc__(self): + return node_classes.Const( + value=self._instance.parent.doc, parent=self._instance + ) + + +class AsyncGeneratorModel(GeneratorModel): + def __new__(cls, *args, **kwargs): + # Append the values from the AGeneratorType unto this object. + ret = super().__new__(cls, *args, **kwargs) + astroid_builtins = astroid.MANAGER.builtins_module + generator = astroid_builtins.get("async_generator") + if generator is None: + # Make it backward compatible. + generator = astroid_builtins.get("generator") + + for name, values in generator.locals.items(): + method = values[0] + patched = lambda cls, meth=method: meth + + setattr(type(ret), IMPL_PREFIX + name, property(patched)) + + return ret + + +class InstanceModel(ObjectModel): + @property + def attr___class__(self): + return self._instance._proxied + + @property + def attr___module__(self): + return node_classes.Const(self._instance.root().qname()) + + @property + def attr___doc__(self): + return node_classes.Const(self._instance.doc) + + @property + def attr___dict__(self): + return _dunder_dict(self._instance, self._instance.instance_attrs) + + +# Exception instances + + +class ExceptionInstanceModel(InstanceModel): + @property + def attr_args(self): + message = node_classes.Const("") + args = node_classes.Tuple(parent=self._instance) + args.postinit((message,)) + return args + + @property + def attr___traceback__(self): + builtins_ast_module = astroid.MANAGER.builtins_module + traceback_type = builtins_ast_module[types.TracebackType.__name__] + return traceback_type.instantiate_class() + + +class SyntaxErrorInstanceModel(ExceptionInstanceModel): + @property + def attr_text(self): + return node_classes.Const("") + + +class OSErrorInstanceModel(ExceptionInstanceModel): + @property + def attr_filename(self): + return node_classes.Const("") + + @property + def attr_errno(self): + return node_classes.Const(0) + + @property + def attr_strerror(self): + return node_classes.Const("") + + attr_filename2 = attr_filename + + +class ImportErrorInstanceModel(ExceptionInstanceModel): + @property + def attr_name(self): + return node_classes.Const("") + + @property + def attr_path(self): + return node_classes.Const("") + + +BUILTIN_EXCEPTIONS = { + "builtins.SyntaxError": SyntaxErrorInstanceModel, + "builtins.ImportError": ImportErrorInstanceModel, + # These are all similar to OSError in terms of attributes + "builtins.OSError": OSErrorInstanceModel, + "builtins.BlockingIOError": OSErrorInstanceModel, + "builtins.BrokenPipeError": OSErrorInstanceModel, + "builtins.ChildProcessError": OSErrorInstanceModel, + "builtins.ConnectionAbortedError": OSErrorInstanceModel, + "builtins.ConnectionError": OSErrorInstanceModel, + "builtins.ConnectionRefusedError": OSErrorInstanceModel, + "builtins.ConnectionResetError": OSErrorInstanceModel, + "builtins.FileExistsError": OSErrorInstanceModel, + "builtins.FileNotFoundError": OSErrorInstanceModel, + "builtins.InterruptedError": OSErrorInstanceModel, + "builtins.IsADirectoryError": OSErrorInstanceModel, + "builtins.NotADirectoryError": OSErrorInstanceModel, + "builtins.PermissionError": OSErrorInstanceModel, + "builtins.ProcessLookupError": OSErrorInstanceModel, + "builtins.TimeoutError": OSErrorInstanceModel, +} + + +class DictModel(ObjectModel): + @property + def attr___class__(self): + return self._instance._proxied + + def _generic_dict_attribute(self, obj, name): + """Generate a bound method that can infer the given *obj*.""" + + class DictMethodBoundMethod(astroid.BoundMethod): + def infer_call_result(self, caller, context=None): + yield obj + + meth = next(self._instance._proxied.igetattr(name)) + return DictMethodBoundMethod(proxy=meth, bound=self._instance) + + @property + def attr_items(self): + elems = [] + obj = node_classes.List(parent=self._instance) + for key, value in self._instance.items: + elem = node_classes.Tuple(parent=obj) + elem.postinit((key, value)) + elems.append(elem) + obj.postinit(elts=elems) + + # pylint: disable=import-outside-toplevel; circular import + from astroid import objects + + obj = objects.DictItems(obj) + return self._generic_dict_attribute(obj, "items") + + @property + def attr_keys(self): + keys = [key for (key, _) in self._instance.items] + obj = node_classes.List(parent=self._instance) + obj.postinit(elts=keys) + + # pylint: disable=import-outside-toplevel; circular import + from astroid import objects + + obj = objects.DictKeys(obj) + return self._generic_dict_attribute(obj, "keys") + + @property + def attr_values(self): + + values = [value for (_, value) in self._instance.items] + obj = node_classes.List(parent=self._instance) + obj.postinit(values) + + # pylint: disable=import-outside-toplevel; circular import + from astroid import objects + + obj = objects.DictValues(obj) + return self._generic_dict_attribute(obj, "values") + + +class PropertyModel(ObjectModel): + """Model for a builtin property""" + + # pylint: disable=import-outside-toplevel + def _init_function(self, name): + from astroid.node_classes import Arguments + from astroid.scoped_nodes import FunctionDef + + args = Arguments() + args.postinit( + args=[], + defaults=[], + kwonlyargs=[], + kw_defaults=[], + annotations=[], + posonlyargs=[], + posonlyargs_annotations=[], + kwonlyargs_annotations=[], + ) + + function = FunctionDef(name=name, parent=self._instance) + + function.postinit(args=args, body=[]) + return function + + @property + def attr_fget(self): + from astroid.scoped_nodes import FunctionDef + + func = self._instance + + class PropertyFuncAccessor(FunctionDef): + def infer_call_result(self, caller=None, context=None): + nonlocal func + if caller and len(caller.args) != 1: + raise exceptions.InferenceError( + "fget() needs a single argument", target=self, context=context + ) + + yield from func.function.infer_call_result( + caller=caller, context=context + ) + + property_accessor = PropertyFuncAccessor(name="fget", parent=self._instance) + property_accessor.postinit(args=func.args, body=func.body) + return property_accessor + + @property + def attr_setter(self): + return self._init_function("setter") + + @property + def attr_deleter(self): + return self._init_function("deleter") + + @property + def attr_getter(self): + return self._init_function("getter") + + # pylint: enable=import-outside-toplevel diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/manager.py b/Display/.venv/lib/python3.7/site-packages/astroid/manager.py new file mode 100644 index 0000000..82208ad --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/manager.py @@ -0,0 +1,350 @@ +# Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2014-2019 Claudiu Popa +# Copyright (c) 2014 BioGeek +# Copyright (c) 2014 Google, Inc. +# Copyright (c) 2014 Eevee (Alex Munroe) +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2016 Derek Gustafson +# Copyright (c) 2017 Iva Miholic +# Copyright (c) 2018 Bryce Guinta +# Copyright (c) 2018 Nick Drozd +# Copyright (c) 2019 Raphael Gaschignard +# Copyright (c) 2020 Anubhav <35621759+anubh-v@users.noreply.github.com> +# Copyright (c) 2020 Ashley Whetter + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""astroid manager: avoid multiple astroid build of a same module when +possible by providing a class responsible to get astroid representation +from various source and using a cache of built modules) +""" + +import os +import zipimport + +from astroid import exceptions +from astroid.interpreter._import import spec +from astroid import modutils +from astroid import transforms + + +ZIP_IMPORT_EXTS = (".zip", ".egg", ".whl") + + +def safe_repr(obj): + try: + return repr(obj) + except Exception: # pylint: disable=broad-except + return "???" + + +class AstroidManager: + """the astroid manager, responsible to build astroid from files + or modules. + + Use the Borg pattern. + """ + + name = "astroid loader" + brain = {} + + def __init__(self): + self.__dict__ = AstroidManager.brain + if not self.__dict__: + # NOTE: cache entries are added by the [re]builder + self.astroid_cache = {} + self._mod_file_cache = {} + self._failed_import_hooks = [] + self.always_load_extensions = False + self.optimize_ast = False + self.extension_package_whitelist = set() + self._transform = transforms.TransformVisitor() + + # Export these APIs for convenience + self.register_transform = self._transform.register_transform + self.unregister_transform = self._transform.unregister_transform + self.max_inferable_values = 100 + + @property + def builtins_module(self): + return self.astroid_cache["builtins"] + + def visit_transforms(self, node): + """Visit the transforms and apply them to the given *node*.""" + return self._transform.visit(node) + + def ast_from_file(self, filepath, modname=None, fallback=True, source=False): + """given a module name, return the astroid object""" + try: + filepath = modutils.get_source_file(filepath, include_no_ext=True) + source = True + except modutils.NoSourceFile: + pass + if modname is None: + try: + modname = ".".join(modutils.modpath_from_file(filepath)) + except ImportError: + modname = filepath + if ( + modname in self.astroid_cache + and self.astroid_cache[modname].file == filepath + ): + return self.astroid_cache[modname] + if source: + # pylint: disable=import-outside-toplevel; circular import + from astroid.builder import AstroidBuilder + + return AstroidBuilder(self).file_build(filepath, modname) + if fallback and modname: + return self.ast_from_module_name(modname) + raise exceptions.AstroidBuildingError( + "Unable to build an AST for {path}.", path=filepath + ) + + def ast_from_string(self, data, modname="", filepath=None): + """ Given some source code as a string, return its corresponding astroid object""" + # pylint: disable=import-outside-toplevel; circular import + from astroid.builder import AstroidBuilder + + return AstroidBuilder(self).string_build(data, modname, filepath) + + def _build_stub_module(self, modname): + # pylint: disable=import-outside-toplevel; circular import + from astroid.builder import AstroidBuilder + + return AstroidBuilder(self).string_build("", modname) + + def _build_namespace_module(self, modname, path): + # pylint: disable=import-outside-toplevel; circular import + from astroid.builder import build_namespace_package_module + + return build_namespace_package_module(modname, path) + + def _can_load_extension(self, modname): + if self.always_load_extensions: + return True + if modutils.is_standard_module(modname): + return True + parts = modname.split(".") + return any( + ".".join(parts[:x]) in self.extension_package_whitelist + for x in range(1, len(parts) + 1) + ) + + def ast_from_module_name(self, modname, context_file=None): + """given a module name, return the astroid object""" + if modname in self.astroid_cache: + return self.astroid_cache[modname] + if modname == "__main__": + return self._build_stub_module(modname) + if context_file: + old_cwd = os.getcwd() + os.chdir(os.path.dirname(context_file)) + try: + found_spec = self.file_from_module_name(modname, context_file) + if found_spec.type == spec.ModuleType.PY_ZIPMODULE: + module = self.zip_import_data(found_spec.location) + if module is not None: + return module + + elif found_spec.type in ( + spec.ModuleType.C_BUILTIN, + spec.ModuleType.C_EXTENSION, + ): + if ( + found_spec.type == spec.ModuleType.C_EXTENSION + and not self._can_load_extension(modname) + ): + return self._build_stub_module(modname) + try: + module = modutils.load_module_from_name(modname) + except Exception as ex: + raise exceptions.AstroidImportError( + "Loading {modname} failed with:\n{error}", + modname=modname, + path=found_spec.location, + ) from ex + return self.ast_from_module(module, modname) + + elif found_spec.type == spec.ModuleType.PY_COMPILED: + raise exceptions.AstroidImportError( + "Unable to load compiled module {modname}.", + modname=modname, + path=found_spec.location, + ) + + elif found_spec.type == spec.ModuleType.PY_NAMESPACE: + return self._build_namespace_module( + modname, found_spec.submodule_search_locations + ) + elif found_spec.type == spec.ModuleType.PY_FROZEN: + return self._build_stub_module(modname) + + if found_spec.location is None: + raise exceptions.AstroidImportError( + "Can't find a file for module {modname}.", modname=modname + ) + + return self.ast_from_file(found_spec.location, modname, fallback=False) + except exceptions.AstroidBuildingError as e: + for hook in self._failed_import_hooks: + try: + return hook(modname) + except exceptions.AstroidBuildingError: + pass + raise e + finally: + if context_file: + os.chdir(old_cwd) + + def zip_import_data(self, filepath): + if zipimport is None: + return None + + # pylint: disable=import-outside-toplevel; circular import + from astroid.builder import AstroidBuilder + + builder = AstroidBuilder(self) + for ext in ZIP_IMPORT_EXTS: + try: + eggpath, resource = filepath.rsplit(ext + os.path.sep, 1) + except ValueError: + continue + try: + importer = zipimport.zipimporter(eggpath + ext) + zmodname = resource.replace(os.path.sep, ".") + if importer.is_package(resource): + zmodname = zmodname + ".__init__" + module = builder.string_build( + importer.get_source(resource), zmodname, filepath + ) + return module + except Exception: # pylint: disable=broad-except + continue + return None + + def file_from_module_name(self, modname, contextfile): + try: + value = self._mod_file_cache[(modname, contextfile)] + except KeyError: + try: + value = modutils.file_info_from_modpath( + modname.split("."), context_file=contextfile + ) + except ImportError as ex: + value = exceptions.AstroidImportError( + "Failed to import module {modname} with error:\n{error}.", + modname=modname, + error=ex, + ) + self._mod_file_cache[(modname, contextfile)] = value + if isinstance(value, exceptions.AstroidBuildingError): + raise value + return value + + def ast_from_module(self, module, modname=None): + """given an imported module, return the astroid object""" + modname = modname or module.__name__ + if modname in self.astroid_cache: + return self.astroid_cache[modname] + try: + # some builtin modules don't have __file__ attribute + filepath = module.__file__ + if modutils.is_python_source(filepath): + return self.ast_from_file(filepath, modname) + except AttributeError: + pass + + # pylint: disable=import-outside-toplevel; circular import + from astroid.builder import AstroidBuilder + + return AstroidBuilder(self).module_build(module, modname) + + def ast_from_class(self, klass, modname=None): + """get astroid for the given class""" + if modname is None: + try: + modname = klass.__module__ + except AttributeError as exc: + raise exceptions.AstroidBuildingError( + "Unable to get module for class {class_name}.", + cls=klass, + class_repr=safe_repr(klass), + modname=modname, + ) from exc + modastroid = self.ast_from_module_name(modname) + return modastroid.getattr(klass.__name__)[0] # XXX + + def infer_ast_from_something(self, obj, context=None): + """infer astroid for the given class""" + if hasattr(obj, "__class__") and not isinstance(obj, type): + klass = obj.__class__ + else: + klass = obj + try: + modname = klass.__module__ + except AttributeError as exc: + raise exceptions.AstroidBuildingError( + "Unable to get module for {class_repr}.", + cls=klass, + class_repr=safe_repr(klass), + ) from exc + except Exception as exc: + raise exceptions.AstroidImportError( + "Unexpected error while retrieving module for {class_repr}:\n" + "{error}", + cls=klass, + class_repr=safe_repr(klass), + ) from exc + try: + name = klass.__name__ + except AttributeError as exc: + raise exceptions.AstroidBuildingError( + "Unable to get name for {class_repr}:\n", + cls=klass, + class_repr=safe_repr(klass), + ) from exc + except Exception as exc: + raise exceptions.AstroidImportError( + "Unexpected error while retrieving name for {class_repr}:\n" "{error}", + cls=klass, + class_repr=safe_repr(klass), + ) from exc + # take care, on living object __module__ is regularly wrong :( + modastroid = self.ast_from_module_name(modname) + if klass is obj: + for inferred in modastroid.igetattr(name, context): + yield inferred + else: + for inferred in modastroid.igetattr(name, context): + yield inferred.instantiate_class() + + def register_failed_import_hook(self, hook): + """Registers a hook to resolve imports that cannot be found otherwise. + + `hook` must be a function that accepts a single argument `modname` which + contains the name of the module or package that could not be imported. + If `hook` can resolve the import, must return a node of type `astroid.Module`, + otherwise, it must raise `AstroidBuildingError`. + """ + self._failed_import_hooks.append(hook) + + def cache_module(self, module): + """Cache a module if no module with the same name is known yet.""" + self.astroid_cache.setdefault(module.name, module) + + def bootstrap(self): + """Bootstrap the required AST modules needed for the manager to work + + The bootstrap usually involves building the AST for the builtins + module, which is required by the rest of astroid to work correctly. + """ + from astroid import raw_building # pylint: disable=import-outside-toplevel + + raw_building._astroid_bootstrapping() + + def clear_cache(self): + """Clear the underlying cache. Also bootstraps the builtins module.""" + self.astroid_cache.clear() + self.bootstrap() diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/mixins.py b/Display/.venv/lib/python3.7/site-packages/astroid/mixins.py new file mode 100644 index 0000000..497a840 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/mixins.py @@ -0,0 +1,160 @@ +# Copyright (c) 2010-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2014-2016, 2018 Claudiu Popa +# Copyright (c) 2014 Google, Inc. +# Copyright (c) 2014 Eevee (Alex Munroe) +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2015 Florian Bruhin +# Copyright (c) 2016 Jakub Wilk +# Copyright (c) 2018 Nick Drozd + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""This module contains some mixins for the different nodes. +""" +import itertools + +from astroid import decorators +from astroid import exceptions + + +class BlockRangeMixIn: + """override block range """ + + @decorators.cachedproperty + def blockstart_tolineno(self): + return self.lineno + + def _elsed_block_range(self, lineno, orelse, last=None): + """handle block line numbers range for try/finally, for, if and while + statements + """ + if lineno == self.fromlineno: + return lineno, lineno + if orelse: + if lineno >= orelse[0].fromlineno: + return lineno, orelse[-1].tolineno + return lineno, orelse[0].fromlineno - 1 + return lineno, last or self.tolineno + + +class FilterStmtsMixin: + """Mixin for statement filtering and assignment type""" + + def _get_filtered_stmts(self, _, node, _stmts, mystmt): + """method used in _filter_stmts to get statements and trigger break""" + if self.statement() is mystmt: + # original node's statement is the assignment, only keep + # current node (gen exp, list comp) + return [node], True + return _stmts, False + + def assign_type(self): + return self + + +class AssignTypeMixin: + def assign_type(self): + return self + + def _get_filtered_stmts(self, lookup_node, node, _stmts, mystmt): + """method used in filter_stmts""" + if self is mystmt: + return _stmts, True + if self.statement() is mystmt: + # original node's statement is the assignment, only keep + # current node (gen exp, list comp) + return [node], True + return _stmts, False + + +class ParentAssignTypeMixin(AssignTypeMixin): + def assign_type(self): + return self.parent.assign_type() + + +class ImportFromMixin(FilterStmtsMixin): + """MixIn for From and Import Nodes""" + + def _infer_name(self, frame, name): + return name + + def do_import_module(self, modname=None): + """return the ast for a module whose name is imported by + """ + # handle special case where we are on a package node importing a module + # using the same name as the package, which may end in an infinite loop + # on relative imports + # XXX: no more needed ? + mymodule = self.root() + level = getattr(self, "level", None) # Import as no level + if modname is None: + modname = self.modname + # XXX we should investigate deeper if we really want to check + # importing itself: modname and mymodule.name be relative or absolute + if mymodule.relative_to_absolute_name(modname, level) == mymodule.name: + # FIXME: we used to raise InferenceError here, but why ? + return mymodule + + return mymodule.import_module( + modname, level=level, relative_only=level and level >= 1 + ) + + def real_name(self, asname): + """get name from 'as' name""" + for name, _asname in self.names: + if name == "*": + return asname + if not _asname: + name = name.split(".", 1)[0] + _asname = name + if asname == _asname: + return name + raise exceptions.AttributeInferenceError( + "Could not find original name for {attribute} in {target!r}", + target=self, + attribute=asname, + ) + + +class MultiLineBlockMixin: + """Mixin for nodes with multi-line blocks, e.g. For and FunctionDef. + Note that this does not apply to every node with a `body` field. + For instance, an If node has a multi-line body, but the body of an + IfExpr is not multi-line, and hence cannot contain Return nodes, + Assign nodes, etc. + """ + + @decorators.cachedproperty + def _multi_line_blocks(self): + return tuple(getattr(self, field) for field in self._multi_line_block_fields) + + def _get_return_nodes_skip_functions(self): + for block in self._multi_line_blocks: + for child_node in block: + if child_node.is_function: + continue + yield from child_node._get_return_nodes_skip_functions() + + def _get_yield_nodes_skip_lambdas(self): + for block in self._multi_line_blocks: + for child_node in block: + if child_node.is_lambda: + continue + yield from child_node._get_yield_nodes_skip_lambdas() + + @decorators.cached + def _get_assign_nodes(self): + children_assign_nodes = ( + child_node._get_assign_nodes() + for block in self._multi_line_blocks + for child_node in block + ) + return list(itertools.chain.from_iterable(children_assign_nodes)) + + +class NoChildrenMixin: + """Mixin for nodes with no children, e.g. Pass.""" + + def get_children(self): + yield from () diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/modutils.py b/Display/.venv/lib/python3.7/site-packages/astroid/modutils.py new file mode 100644 index 0000000..4e6ed86 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/modutils.py @@ -0,0 +1,690 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2014-2018, 2020 Claudiu Popa +# Copyright (c) 2014 Google, Inc. +# Copyright (c) 2014 Denis Laxalde +# Copyright (c) 2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2014 Eevee (Alex Munroe) +# Copyright (c) 2015 Florian Bruhin +# Copyright (c) 2015 Radosław Ganczarek +# Copyright (c) 2016 Derek Gustafson +# Copyright (c) 2016 Jakub Wilk +# Copyright (c) 2016 Ceridwen +# Copyright (c) 2018 Ville Skyttä +# Copyright (c) 2018 Mario Corchero +# Copyright (c) 2018 Mario Corchero +# Copyright (c) 2018 Anthony Sottile +# Copyright (c) 2019 Hugo van Kemenade +# Copyright (c) 2019 markmcclain +# Copyright (c) 2019 BasPH + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""Python modules manipulation utility functions. + +:type PY_SOURCE_EXTS: tuple(str) +:var PY_SOURCE_EXTS: list of possible python source file extension + +:type STD_LIB_DIRS: set of str +:var STD_LIB_DIRS: directories where standard modules are located + +:type BUILTIN_MODULES: dict +:var BUILTIN_MODULES: dictionary with builtin module names has key +""" +import imp +import os +import platform +import sys +import itertools +from distutils.sysconfig import get_python_lib # pylint: disable=import-error + +# pylint: disable=import-error, no-name-in-module +from distutils.errors import DistutilsPlatformError + +# distutils is replaced by virtualenv with a module that does +# weird path manipulations in order to get to the +# real distutils module. +from typing import Optional, List + +from .interpreter._import import spec +from .interpreter._import import util + +if sys.platform.startswith("win"): + PY_SOURCE_EXTS = ("py", "pyw") + PY_COMPILED_EXTS = ("dll", "pyd") +else: + PY_SOURCE_EXTS = ("py",) + PY_COMPILED_EXTS = ("so",) + + +try: + # The explicit sys.prefix is to work around a patch in virtualenv that + # replaces the 'real' sys.prefix (i.e. the location of the binary) + # with the prefix from which the virtualenv was created. This throws + # off the detection logic for standard library modules, thus the + # workaround. + STD_LIB_DIRS = { + get_python_lib(standard_lib=True, prefix=sys.prefix), + # Take care of installations where exec_prefix != prefix. + get_python_lib(standard_lib=True, prefix=sys.exec_prefix), + get_python_lib(standard_lib=True), + } +# get_python_lib(standard_lib=1) is not available on pypy, set STD_LIB_DIR to +# non-valid path, see https://bugs.pypy.org/issue1164 +except DistutilsPlatformError: + STD_LIB_DIRS = set() + +if os.name == "nt": + STD_LIB_DIRS.add(os.path.join(sys.prefix, "dlls")) + try: + # real_prefix is defined when running inside virtual environments, + # created with the **virtualenv** library. + STD_LIB_DIRS.add(os.path.join(sys.real_prefix, "dlls")) + except AttributeError: + # sys.base_exec_prefix is always defined, but in a virtual environment + # created with the stdlib **venv** module, it points to the original + # installation, if the virtual env is activated. + try: + STD_LIB_DIRS.add(os.path.join(sys.base_exec_prefix, "dlls")) + except AttributeError: + pass + +if platform.python_implementation() == "PyPy": + _root = os.path.join(sys.prefix, "lib_pypy") + STD_LIB_DIRS.add(_root) + try: + # real_prefix is defined when running inside virtualenv. + STD_LIB_DIRS.add(os.path.join(sys.real_prefix, "lib_pypy")) + except AttributeError: + pass + del _root +if os.name == "posix": + # Need the real prefix is we're under a virtualenv, otherwise + # the usual one will do. + try: + prefix = sys.real_prefix + except AttributeError: + prefix = sys.prefix + + def _posix_path(path): + base_python = "python%d.%d" % sys.version_info[:2] + return os.path.join(prefix, path, base_python) + + STD_LIB_DIRS.add(_posix_path("lib")) + if sys.maxsize > 2 ** 32: + # This tries to fix a problem with /usr/lib64 builds, + # where systems are running both 32-bit and 64-bit code + # on the same machine, which reflects into the places where + # standard library could be found. More details can be found + # here http://bugs.python.org/issue1294959. + # An easy reproducing case would be + # https://github.com/PyCQA/pylint/issues/712#issuecomment-163178753 + STD_LIB_DIRS.add(_posix_path("lib64")) + +EXT_LIB_DIRS = {get_python_lib(), get_python_lib(True)} +IS_JYTHON = platform.python_implementation() == "Jython" +BUILTIN_MODULES = dict.fromkeys(sys.builtin_module_names, True) + + +class NoSourceFile(Exception): + """exception raised when we are not able to get a python + source file for a precompiled file + """ + + +def _normalize_path(path): + return os.path.normcase(os.path.abspath(path)) + + +def _canonicalize_path(path): + return os.path.realpath(os.path.expanduser(path)) + + +def _path_from_filename(filename, is_jython=IS_JYTHON): + if not is_jython: + return filename + head, has_pyclass, _ = filename.partition("$py.class") + if has_pyclass: + return head + ".py" + return filename + + +def _handle_blacklist(blacklist, dirnames, filenames): + """remove files/directories in the black list + + dirnames/filenames are usually from os.walk + """ + for norecurs in blacklist: + if norecurs in dirnames: + dirnames.remove(norecurs) + elif norecurs in filenames: + filenames.remove(norecurs) + + +_NORM_PATH_CACHE = {} + + +def _cache_normalize_path(path): + """abspath with caching""" + # _module_file calls abspath on every path in sys.path every time it's + # called; on a larger codebase this easily adds up to half a second just + # assembling path components. This cache alleviates that. + try: + return _NORM_PATH_CACHE[path] + except KeyError: + if not path: # don't cache result for '' + return _normalize_path(path) + result = _NORM_PATH_CACHE[path] = _normalize_path(path) + return result + + +def load_module_from_name(dotted_name, path=None, use_sys=True): + """Load a Python module from its name. + + :type dotted_name: str + :param dotted_name: python name of a module or package + + :type path: list or None + :param path: + optional list of path where the module or package should be + searched (use sys.path if nothing or None is given) + + :type use_sys: bool + :param use_sys: + boolean indicating whether the sys.modules dictionary should be + used or not + + + :raise ImportError: if the module or package is not found + + :rtype: module + :return: the loaded module + """ + return load_module_from_modpath(dotted_name.split("."), path, use_sys) + + +def load_module_from_modpath(parts, path: Optional[List[str]] = None, use_sys=1): + """Load a python module from its split name. + + :type parts: list(str) or tuple(str) + :param parts: + python name of a module or package split on '.' + + :param path: + Optional list of path where the module or package should be + searched (use sys.path if nothing or None is given) + + :type use_sys: bool + :param use_sys: + boolean indicating whether the sys.modules dictionary should be used or not + + :raise ImportError: if the module or package is not found + + :rtype: module + :return: the loaded module + """ + if use_sys: + try: + return sys.modules[".".join(parts)] + except KeyError: + pass + modpath = [] + prevmodule = None + for part in parts: + modpath.append(part) + curname = ".".join(modpath) + module = None + if len(modpath) != len(parts): + # even with use_sys=False, should try to get outer packages from sys.modules + module = sys.modules.get(curname) + elif use_sys: + # because it may have been indirectly loaded through a parent + module = sys.modules.get(curname) + if module is None: + mp_file, mp_filename, mp_desc = imp.find_module(part, path) + module = imp.load_module(curname, mp_file, mp_filename, mp_desc) + # mp_file still needs to be closed. + if mp_file: + mp_file.close() + if prevmodule: + setattr(prevmodule, part, module) + _file = getattr(module, "__file__", "") + prevmodule = module + if not _file and util.is_namespace(curname): + continue + if not _file and len(modpath) != len(parts): + raise ImportError("no module in %s" % ".".join(parts[len(modpath) :])) + path = [os.path.dirname(_file)] + return module + + +def load_module_from_file( + filepath: str, path: Optional[List[str]] = None, use_sys=True +): + """Load a Python module from it's path. + + :type filepath: str + :param filepath: path to the python module or package + + :param Optional[List[str]] path: + Optional list of path where the module or package should be + searched (use sys.path if nothing or None is given) + + :type use_sys: bool + :param use_sys: + boolean indicating whether the sys.modules dictionary should be + used or not + + :raise ImportError: if the module or package is not found + + :rtype: module + :return: the loaded module + """ + modpath = modpath_from_file(filepath) + return load_module_from_modpath(modpath, path, use_sys) + + +def check_modpath_has_init(path, mod_path): + """check there are some __init__.py all along the way""" + modpath = [] + for part in mod_path: + modpath.append(part) + path = os.path.join(path, part) + if not _has_init(path): + old_namespace = util.is_namespace(".".join(modpath)) + if not old_namespace: + return False + return True + + +def _get_relative_base_path(filename, path_to_check): + """Extracts the relative mod path of the file to import from + + Check if a file is within the passed in path and if so, returns the + relative mod path from the one passed in. + + If the filename is no in path_to_check, returns None + + Note this function will look for both abs and realpath of the file, + this allows to find the relative base path even if the file is a + symlink of a file in the passed in path + + Examples: + _get_relative_base_path("/a/b/c/d.py", "/a/b") -> ["c","d"] + _get_relative_base_path("/a/b/c/d.py", "/dev") -> None + """ + importable_path = None + path_to_check = os.path.normcase(path_to_check) + abs_filename = os.path.abspath(filename) + if os.path.normcase(abs_filename).startswith(path_to_check): + importable_path = abs_filename + + real_filename = os.path.realpath(filename) + if os.path.normcase(real_filename).startswith(path_to_check): + importable_path = real_filename + + if importable_path: + base_path = os.path.splitext(importable_path)[0] + relative_base_path = base_path[len(path_to_check) :] + return [pkg for pkg in relative_base_path.split(os.sep) if pkg] + + return None + + +def modpath_from_file_with_callback(filename, path=None, is_package_cb=None): + filename = os.path.expanduser(_path_from_filename(filename)) + for pathname in itertools.chain( + path or [], map(_canonicalize_path, sys.path), sys.path + ): + pathname = _cache_normalize_path(pathname) + if not pathname: + continue + modpath = _get_relative_base_path(filename, pathname) + if not modpath: + continue + if is_package_cb(pathname, modpath[:-1]): + return modpath + + raise ImportError( + "Unable to find module for %s in %s" % (filename, ", \n".join(sys.path)) + ) + + +def modpath_from_file(filename, path=None): + """Get the corresponding split module's name from a filename + + This function will return the name of a module or package split on `.`. + + :type filename: str + :param filename: file's path for which we want the module's name + + :type Optional[List[str]] path: + Optional list of path where the module or package should be + searched (use sys.path if nothing or None is given) + + :raise ImportError: + if the corresponding module's name has not been found + + :rtype: list(str) + :return: the corresponding split module's name + """ + return modpath_from_file_with_callback(filename, path, check_modpath_has_init) + + +def file_from_modpath(modpath, path=None, context_file=None): + return file_info_from_modpath(modpath, path, context_file).location + + +def file_info_from_modpath(modpath, path=None, context_file=None): + """given a mod path (i.e. split module / package name), return the + corresponding file, giving priority to source file over precompiled + file if it exists + + :type modpath: list or tuple + :param modpath: + split module's name (i.e name of a module or package split + on '.') + (this means explicit relative imports that start with dots have + empty strings in this list!) + + :type path: list or None + :param path: + optional list of path where the module or package should be + searched (use sys.path if nothing or None is given) + + :type context_file: str or None + :param context_file: + context file to consider, necessary if the identifier has been + introduced using a relative import unresolvable in the actual + context (i.e. modutils) + + :raise ImportError: if there is no such module in the directory + + :rtype: (str or None, import type) + :return: + the path to the module's file or None if it's an integrated + builtin module such as 'sys' + """ + if context_file is not None: + context = os.path.dirname(context_file) + else: + context = context_file + if modpath[0] == "xml": + # handle _xmlplus + try: + return _spec_from_modpath(["_xmlplus"] + modpath[1:], path, context) + except ImportError: + return _spec_from_modpath(modpath, path, context) + elif modpath == ["os", "path"]: + # FIXME: currently ignoring search_path... + return spec.ModuleSpec( + name="os.path", location=os.path.__file__, module_type=imp.PY_SOURCE + ) + return _spec_from_modpath(modpath, path, context) + + +def get_module_part(dotted_name, context_file=None): + """given a dotted name return the module part of the name : + + >>> get_module_part('astroid.as_string.dump') + 'astroid.as_string' + + :type dotted_name: str + :param dotted_name: full name of the identifier we are interested in + + :type context_file: str or None + :param context_file: + context file to consider, necessary if the identifier has been + introduced using a relative import unresolvable in the actual + context (i.e. modutils) + + + :raise ImportError: if there is no such module in the directory + + :rtype: str or None + :return: + the module part of the name or None if we have not been able at + all to import the given name + + XXX: deprecated, since it doesn't handle package precedence over module + (see #10066) + """ + # os.path trick + if dotted_name.startswith("os.path"): + return "os.path" + parts = dotted_name.split(".") + if context_file is not None: + # first check for builtin module which won't be considered latter + # in that case (path != None) + if parts[0] in BUILTIN_MODULES: + if len(parts) > 2: + raise ImportError(dotted_name) + return parts[0] + # don't use += or insert, we want a new list to be created ! + path = None + starti = 0 + if parts[0] == "": + assert ( + context_file is not None + ), "explicit relative import, but no context_file?" + path = [] # prevent resolving the import non-relatively + starti = 1 + while parts[starti] == "": # for all further dots: change context + starti += 1 + context_file = os.path.dirname(context_file) + for i in range(starti, len(parts)): + try: + file_from_modpath( + parts[starti : i + 1], path=path, context_file=context_file + ) + except ImportError: + if i < max(1, len(parts) - 2): + raise + return ".".join(parts[:i]) + return dotted_name + + +def get_module_files(src_directory, blacklist, list_all=False): + """given a package directory return a list of all available python + module's files in the package and its subpackages + + :type src_directory: str + :param src_directory: + path of the directory corresponding to the package + + :type blacklist: list or tuple + :param blacklist: iterable + list of files or directories to ignore. + + :type list_all: bool + :param list_all: + get files from all paths, including ones without __init__.py + + :rtype: list + :return: + the list of all available python module's files in the package and + its subpackages + """ + files = [] + for directory, dirnames, filenames in os.walk(src_directory): + if directory in blacklist: + continue + _handle_blacklist(blacklist, dirnames, filenames) + # check for __init__.py + if not list_all and "__init__.py" not in filenames: + dirnames[:] = () + continue + for filename in filenames: + if _is_python_file(filename): + src = os.path.join(directory, filename) + files.append(src) + return files + + +def get_source_file(filename, include_no_ext=False): + """given a python module's file name return the matching source file + name (the filename will be returned identically if it's already an + absolute path to a python source file...) + + :type filename: str + :param filename: python module's file name + + + :raise NoSourceFile: if no source file exists on the file system + + :rtype: str + :return: the absolute path of the source file if it exists + """ + filename = os.path.abspath(_path_from_filename(filename)) + base, orig_ext = os.path.splitext(filename) + for ext in PY_SOURCE_EXTS: + source_path = "%s.%s" % (base, ext) + if os.path.exists(source_path): + return source_path + if include_no_ext and not orig_ext and os.path.exists(base): + return base + raise NoSourceFile(filename) + + +def is_python_source(filename): + """ + rtype: bool + return: True if the filename is a python source file + """ + return os.path.splitext(filename)[1][1:] in PY_SOURCE_EXTS + + +def is_standard_module(modname, std_path=None): + """try to guess if a module is a standard python module (by default, + see `std_path` parameter's description) + + :type modname: str + :param modname: name of the module we are interested in + + :type std_path: list(str) or tuple(str) + :param std_path: list of path considered has standard + + + :rtype: bool + :return: + true if the module: + - is located on the path listed in one of the directory in `std_path` + - is a built-in module + """ + modname = modname.split(".")[0] + try: + filename = file_from_modpath([modname]) + except ImportError: + # import failed, i'm probably not so wrong by supposing it's + # not standard... + return False + # modules which are not living in a file are considered standard + # (sys and __builtin__ for instance) + if filename is None: + # we assume there are no namespaces in stdlib + return not util.is_namespace(modname) + filename = _normalize_path(filename) + for path in EXT_LIB_DIRS: + if filename.startswith(_cache_normalize_path(path)): + return False + if std_path is None: + std_path = STD_LIB_DIRS + for path in std_path: + if filename.startswith(_cache_normalize_path(path)): + return True + return False + + +def is_relative(modname, from_file): + """return true if the given module name is relative to the given + file name + + :type modname: str + :param modname: name of the module we are interested in + + :type from_file: str + :param from_file: + path of the module from which modname has been imported + + :rtype: bool + :return: + true if the module has been imported relatively to `from_file` + """ + if not os.path.isdir(from_file): + from_file = os.path.dirname(from_file) + if from_file in sys.path: + return False + try: + stream, _, _ = imp.find_module(modname.split(".")[0], [from_file]) + + # Close the stream to avoid ResourceWarnings. + if stream: + stream.close() + return True + except ImportError: + return False + + +# internal only functions ##################################################### + + +def _spec_from_modpath(modpath, path=None, context=None): + """given a mod path (i.e. split module / package name), return the + corresponding spec + + this function is used internally, see `file_from_modpath`'s + documentation for more information + """ + assert modpath + location = None + if context is not None: + try: + found_spec = spec.find_spec(modpath, [context]) + location = found_spec.location + except ImportError: + found_spec = spec.find_spec(modpath, path) + location = found_spec.location + else: + found_spec = spec.find_spec(modpath, path) + if found_spec.type == spec.ModuleType.PY_COMPILED: + try: + location = get_source_file(found_spec.location) + return found_spec._replace( + location=location, type=spec.ModuleType.PY_SOURCE + ) + except NoSourceFile: + return found_spec._replace(location=location) + elif found_spec.type == spec.ModuleType.C_BUILTIN: + # integrated builtin module + return found_spec._replace(location=None) + elif found_spec.type == spec.ModuleType.PKG_DIRECTORY: + location = _has_init(found_spec.location) + return found_spec._replace(location=location, type=spec.ModuleType.PY_SOURCE) + return found_spec + + +def _is_python_file(filename): + """return true if the given filename should be considered as a python file + + .pyc and .pyo are ignored + """ + return filename.endswith((".py", ".so", ".pyd", ".pyw")) + + +def _has_init(directory): + """if the given directory has a valid __init__ file, return its path, + else return None + """ + mod_or_pack = os.path.join(directory, "__init__") + for ext in PY_SOURCE_EXTS + ("pyc", "pyo"): + if os.path.exists(mod_or_pack + "." + ext): + return mod_or_pack + "." + ext + return None + + +def is_namespace(specobj): + return specobj.type == spec.ModuleType.PY_NAMESPACE + + +def is_directory(specobj): + return specobj.type == spec.ModuleType.PKG_DIRECTORY diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/node_classes.py b/Display/.venv/lib/python3.7/site-packages/astroid/node_classes.py new file mode 100644 index 0000000..621dc5f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/node_classes.py @@ -0,0 +1,4862 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2010 Daniel Harding +# Copyright (c) 2012 FELD Boris +# Copyright (c) 2013-2014 Google, Inc. +# Copyright (c) 2014-2020 Claudiu Popa +# Copyright (c) 2014 Eevee (Alex Munroe) +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2015 Florian Bruhin +# Copyright (c) 2016-2017 Derek Gustafson +# Copyright (c) 2016 Jared Garst +# Copyright (c) 2016 Jakub Wilk +# Copyright (c) 2016 Dave Baum +# Copyright (c) 2017-2020 Ashley Whetter +# Copyright (c) 2017, 2019 Łukasz Rogalski +# Copyright (c) 2017 rr- +# Copyright (c) 2018-2019 hippo91 +# Copyright (c) 2018 Nick Drozd +# Copyright (c) 2018 Ville Skyttä +# Copyright (c) 2018 Bryce Guinta +# Copyright (c) 2018 brendanator +# Copyright (c) 2018 HoverHell +# Copyright (c) 2019 kavins14 +# Copyright (c) 2019 kavins14 + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +# pylint: disable=too-many-lines; https://github.com/PyCQA/astroid/issues/465 + +"""Module for some node classes. More nodes in scoped_nodes.py +""" + +import abc +import builtins as builtins_mod +import itertools +import pprint +import sys +from functools import lru_cache, singledispatch as _singledispatch + +from astroid import as_string +from astroid import bases +from astroid import context as contextmod +from astroid import decorators +from astroid import exceptions +from astroid import manager +from astroid import mixins +from astroid import util + + +BUILTINS = builtins_mod.__name__ +MANAGER = manager.AstroidManager() +PY38 = sys.version_info[:2] >= (3, 8) + + +def _is_const(value): + return isinstance(value, tuple(CONST_CLS)) + + +@decorators.raise_if_nothing_inferred +def unpack_infer(stmt, context=None): + """recursively generate nodes inferred by the given statement. + If the inferred value is a list or a tuple, recurse on the elements + """ + if isinstance(stmt, (List, Tuple)): + for elt in stmt.elts: + if elt is util.Uninferable: + yield elt + continue + yield from unpack_infer(elt, context) + return dict(node=stmt, context=context) + # if inferred is a final node, return it and stop + inferred = next(stmt.infer(context)) + if inferred is stmt: + yield inferred + return dict(node=stmt, context=context) + # else, infer recursively, except Uninferable object that should be returned as is + for inferred in stmt.infer(context): + if inferred is util.Uninferable: + yield inferred + else: + yield from unpack_infer(inferred, context) + + return dict(node=stmt, context=context) + + +def are_exclusive( + stmt1, stmt2, exceptions=None +): # pylint: disable=redefined-outer-name + """return true if the two given statements are mutually exclusive + + `exceptions` may be a list of exception names. If specified, discard If + branches and check one of the statement is in an exception handler catching + one of the given exceptions. + + algorithm : + 1) index stmt1's parents + 2) climb among stmt2's parents until we find a common parent + 3) if the common parent is a If or TryExcept statement, look if nodes are + in exclusive branches + """ + # index stmt1's parents + stmt1_parents = {} + children = {} + node = stmt1.parent + previous = stmt1 + while node: + stmt1_parents[node] = 1 + children[node] = previous + previous = node + node = node.parent + # climb among stmt2's parents until we find a common parent + node = stmt2.parent + previous = stmt2 + while node: + if node in stmt1_parents: + # if the common parent is a If or TryExcept statement, look if + # nodes are in exclusive branches + if isinstance(node, If) and exceptions is None: + if ( + node.locate_child(previous)[1] + is not node.locate_child(children[node])[1] + ): + return True + elif isinstance(node, TryExcept): + c2attr, c2node = node.locate_child(previous) + c1attr, c1node = node.locate_child(children[node]) + if c1node is not c2node: + first_in_body_caught_by_handlers = ( + c2attr == "handlers" + and c1attr == "body" + and previous.catch(exceptions) + ) + second_in_body_caught_by_handlers = ( + c2attr == "body" + and c1attr == "handlers" + and children[node].catch(exceptions) + ) + first_in_else_other_in_handlers = ( + c2attr == "handlers" and c1attr == "orelse" + ) + second_in_else_other_in_handlers = ( + c2attr == "orelse" and c1attr == "handlers" + ) + if any( + ( + first_in_body_caught_by_handlers, + second_in_body_caught_by_handlers, + first_in_else_other_in_handlers, + second_in_else_other_in_handlers, + ) + ): + return True + elif c2attr == "handlers" and c1attr == "handlers": + return previous is not children[node] + return False + previous = node + node = node.parent + return False + + +# getitem() helpers. + +_SLICE_SENTINEL = object() + + +def _slice_value(index, context=None): + """Get the value of the given slice index.""" + + if isinstance(index, Const): + if isinstance(index.value, (int, type(None))): + return index.value + elif index is None: + return None + else: + # Try to infer what the index actually is. + # Since we can't return all the possible values, + # we'll stop at the first possible value. + try: + inferred = next(index.infer(context=context)) + except exceptions.InferenceError: + pass + else: + if isinstance(inferred, Const): + if isinstance(inferred.value, (int, type(None))): + return inferred.value + + # Use a sentinel, because None can be a valid + # value that this function can return, + # as it is the case for unspecified bounds. + return _SLICE_SENTINEL + + +def _infer_slice(node, context=None): + lower = _slice_value(node.lower, context) + upper = _slice_value(node.upper, context) + step = _slice_value(node.step, context) + if all(elem is not _SLICE_SENTINEL for elem in (lower, upper, step)): + return slice(lower, upper, step) + + raise exceptions.AstroidTypeError( + message="Could not infer slice used in subscript", + node=node, + index=node.parent, + context=context, + ) + + +def _container_getitem(instance, elts, index, context=None): + """Get a slice or an item, using the given *index*, for the given sequence.""" + try: + if isinstance(index, Slice): + index_slice = _infer_slice(index, context=context) + new_cls = instance.__class__() + new_cls.elts = elts[index_slice] + new_cls.parent = instance.parent + return new_cls + if isinstance(index, Const): + return elts[index.value] + except IndexError as exc: + raise exceptions.AstroidIndexError( + message="Index {index!s} out of range", + node=instance, + index=index, + context=context, + ) from exc + except TypeError as exc: + raise exceptions.AstroidTypeError( + message="Type error {error!r}", node=instance, index=index, context=context + ) from exc + + raise exceptions.AstroidTypeError("Could not use %s as subscript index" % index) + + +OP_PRECEDENCE = { + op: precedence + for precedence, ops in enumerate( + [ + ["Lambda"], # lambda x: x + 1 + ["IfExp"], # 1 if True else 2 + ["or"], + ["and"], + ["not"], + ["Compare"], # in, not in, is, is not, <, <=, >, >=, !=, == + ["|"], + ["^"], + ["&"], + ["<<", ">>"], + ["+", "-"], + ["*", "@", "/", "//", "%"], + ["UnaryOp"], # +, -, ~ + ["**"], + ["Await"], + ] + ) + for op in ops +} + + +class NodeNG: + """ A node of the new Abstract Syntax Tree (AST). + + This is the base class for all Astroid node classes. + """ + + is_statement = False + """Whether this node indicates a statement. + + :type: bool + """ + optional_assign = False # True for For (and for Comprehension if py <3.0) + """Whether this node optionally assigns a variable. + + This is for loop assignments because loop won't necessarily perform an + assignment if the loop has no iterations. + This is also the case from comprehensions in Python 2. + + :type: bool + """ + is_function = False # True for FunctionDef nodes + """Whether this node indicates a function. + + :type: bool + """ + is_lambda = False + # Attributes below are set by the builder module or by raw factories + lineno = None + """The line that this node appears on in the source code. + + :type: int or None + """ + col_offset = None + """The column that this node appears on in the source code. + + :type: int or None + """ + parent = None + """The parent node in the syntax tree. + + :type: NodeNG or None + """ + _astroid_fields = () + """Node attributes that contain child nodes. + + This is redefined in most concrete classes. + + :type: tuple(str) + """ + _other_fields = () + """Node attributes that do not contain child nodes. + + :type: tuple(str) + """ + _other_other_fields = () + """Attributes that contain AST-dependent fields. + + :type: tuple(str) + """ + # instance specific inference function infer(node, context) + _explicit_inference = None + + def __init__(self, lineno=None, col_offset=None, parent=None): + """ + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.lineno = lineno + self.col_offset = col_offset + self.parent = parent + + def infer(self, context=None, **kwargs): + """Get a generator of the inferred values. + + This is the main entry point to the inference system. + + .. seealso:: :ref:`inference` + + If the instance has some explicit inference function set, it will be + called instead of the default interface. + + :returns: The inferred values. + :rtype: iterable + """ + if context is not None: + context = context.extra_context.get(self, context) + if self._explicit_inference is not None: + # explicit_inference is not bound, give it self explicitly + try: + # pylint: disable=not-callable + return self._explicit_inference(self, context, **kwargs) + except exceptions.UseInferenceDefault: + pass + + if not context: + return self._infer(context, **kwargs) + + key = (self, context.lookupname, context.callcontext, context.boundnode) + if key in context.inferred: + return iter(context.inferred[key]) + + gen = context.cache_generator(key, self._infer(context, **kwargs)) + return util.limit_inference(gen, MANAGER.max_inferable_values) + + def _repr_name(self): + """Get a name for nice representation. + + This is either :attr:`name`, :attr:`attrname`, or the empty string. + + :returns: The nice name. + :rtype: str + """ + names = {"name", "attrname"} + if all(name not in self._astroid_fields for name in names): + return getattr(self, "name", getattr(self, "attrname", "")) + return "" + + def __str__(self): + rname = self._repr_name() + cname = type(self).__name__ + if rname: + string = "%(cname)s.%(rname)s(%(fields)s)" + alignment = len(cname) + len(rname) + 2 + else: + string = "%(cname)s(%(fields)s)" + alignment = len(cname) + 1 + result = [] + for field in self._other_fields + self._astroid_fields: + value = getattr(self, field) + width = 80 - len(field) - alignment + lines = pprint.pformat(value, indent=2, width=width).splitlines(True) + + inner = [lines[0]] + for line in lines[1:]: + inner.append(" " * alignment + line) + result.append("%s=%s" % (field, "".join(inner))) + + return string % { + "cname": cname, + "rname": rname, + "fields": (",\n" + " " * alignment).join(result), + } + + def __repr__(self): + rname = self._repr_name() + if rname: + string = "<%(cname)s.%(rname)s l.%(lineno)s at 0x%(id)x>" + else: + string = "<%(cname)s l.%(lineno)s at 0x%(id)x>" + return string % { + "cname": type(self).__name__, + "rname": rname, + "lineno": self.fromlineno, + "id": id(self), + } + + def accept(self, visitor): + """Visit this node using the given visitor.""" + func = getattr(visitor, "visit_" + self.__class__.__name__.lower()) + return func(self) + + def get_children(self): + """Get the child nodes below this node. + + :returns: The children. + :rtype: iterable(NodeNG) + """ + for field in self._astroid_fields: + attr = getattr(self, field) + if attr is None: + continue + if isinstance(attr, (list, tuple)): + yield from attr + else: + yield attr + + def last_child(self): + """An optimized version of list(get_children())[-1] + + :returns: The last child, or None if no children exist. + :rtype: NodeNG or None + """ + for field in self._astroid_fields[::-1]: + attr = getattr(self, field) + if not attr: # None or empty listy / tuple + continue + if isinstance(attr, (list, tuple)): + return attr[-1] + + return attr + return None + + def parent_of(self, node): + """Check if this node is the parent of the given node. + + :param node: The node to check if it is the child. + :type node: NodeNG + + :returns: True if this node is the parent of the given node, + False otherwise. + :rtype: bool + """ + parent = node.parent + while parent is not None: + if self is parent: + return True + parent = parent.parent + return False + + def statement(self): + """The first parent node, including self, marked as statement node. + + :returns: The first parent statement. + :rtype: NodeNG + """ + if self.is_statement: + return self + return self.parent.statement() + + def frame(self): + """The first parent frame node. + + A frame node is a :class:`Module`, :class:`FunctionDef`, + or :class:`ClassDef`. + + :returns: The first parent frame node. + :rtype: Module or FunctionDef or ClassDef + """ + return self.parent.frame() + + def scope(self): + """The first parent node defining a new scope. + + :returns: The first parent scope node. + :rtype: Module or FunctionDef or ClassDef or Lambda or GenExpr + """ + if self.parent: + return self.parent.scope() + return None + + def root(self): + """Return the root node of the syntax tree. + + :returns: The root node. + :rtype: Module + """ + if self.parent: + return self.parent.root() + return self + + def child_sequence(self, child): + """Search for the sequence that contains this child. + + :param child: The child node to search sequences for. + :type child: NodeNG + + :returns: The sequence containing the given child node. + :rtype: iterable(NodeNG) + + :raises AstroidError: If no sequence could be found that contains + the given child. + """ + for field in self._astroid_fields: + node_or_sequence = getattr(self, field) + if node_or_sequence is child: + return [node_or_sequence] + # /!\ compiler.ast Nodes have an __iter__ walking over child nodes + if ( + isinstance(node_or_sequence, (tuple, list)) + and child in node_or_sequence + ): + return node_or_sequence + + msg = "Could not find %s in %s's children" + raise exceptions.AstroidError(msg % (repr(child), repr(self))) + + def locate_child(self, child): + """Find the field of this node that contains the given child. + + :param child: The child node to search fields for. + :type child: NodeNG + + :returns: A tuple of the name of the field that contains the child, + and the sequence or node that contains the child node. + :rtype: tuple(str, iterable(NodeNG) or NodeNG) + + :raises AstroidError: If no field could be found that contains + the given child. + """ + for field in self._astroid_fields: + node_or_sequence = getattr(self, field) + # /!\ compiler.ast Nodes have an __iter__ walking over child nodes + if child is node_or_sequence: + return field, child + if ( + isinstance(node_or_sequence, (tuple, list)) + and child in node_or_sequence + ): + return field, node_or_sequence + msg = "Could not find %s in %s's children" + raise exceptions.AstroidError(msg % (repr(child), repr(self))) + + # FIXME : should we merge child_sequence and locate_child ? locate_child + # is only used in are_exclusive, child_sequence one time in pylint. + + def next_sibling(self): + """The next sibling statement node. + + :returns: The next sibling statement node. + :rtype: NodeNG or None + """ + return self.parent.next_sibling() + + def previous_sibling(self): + """The previous sibling statement. + + :returns: The previous sibling statement node. + :rtype: NodeNG or None + """ + return self.parent.previous_sibling() + + # these are lazy because they're relatively expensive to compute for every + # single node, and they rarely get looked at + + @decorators.cachedproperty + def fromlineno(self): + """The first line that this node appears on in the source code. + + :type: int or None + """ + if self.lineno is None: + return self._fixed_source_line() + + return self.lineno + + @decorators.cachedproperty + def tolineno(self): + """The last line that this node appears on in the source code. + + :type: int or None + """ + if not self._astroid_fields: + # can't have children + lastchild = None + else: + lastchild = self.last_child() + if lastchild is None: + return self.fromlineno + + return lastchild.tolineno + + def _fixed_source_line(self): + """Attempt to find the line that this node appears on. + + We need this method since not all nodes have :attr:`lineno` set. + + :returns: The line number of this node, + or None if this could not be determined. + :rtype: int or None + """ + line = self.lineno + _node = self + try: + while line is None: + _node = next(_node.get_children()) + line = _node.lineno + except StopIteration: + _node = self.parent + while _node and line is None: + line = _node.lineno + _node = _node.parent + return line + + def block_range(self, lineno): + """Get a range from the given line number to where this node ends. + + :param lineno: The line number to start the range at. + :type lineno: int + + :returns: The range of line numbers that this node belongs to, + starting at the given line number. + :rtype: tuple(int, int or None) + """ + return lineno, self.tolineno + + def set_local(self, name, stmt): + """Define that the given name is declared in the given statement node. + + This definition is stored on the parent scope node. + + .. seealso:: :meth:`scope` + + :param name: The name that is being defined. + :type name: str + + :param stmt: The statement that defines the given name. + :type stmt: NodeNG + """ + self.parent.set_local(name, stmt) + + def nodes_of_class(self, klass, skip_klass=None): + """Get the nodes (including this one or below) of the given types. + + :param klass: The types of node to search for. + :type klass: builtins.type or tuple(builtins.type) + + :param skip_klass: The types of node to ignore. This is useful to ignore + subclasses of :attr:`klass`. + :type skip_klass: builtins.type or tuple(builtins.type) + + :returns: The node of the given types. + :rtype: iterable(NodeNG) + """ + if isinstance(self, klass): + yield self + + if skip_klass is None: + for child_node in self.get_children(): + yield from child_node.nodes_of_class(klass, skip_klass) + + return + + for child_node in self.get_children(): + if isinstance(child_node, skip_klass): + continue + yield from child_node.nodes_of_class(klass, skip_klass) + + @decorators.cached + def _get_assign_nodes(self): + return [] + + def _get_name_nodes(self): + for child_node in self.get_children(): + yield from child_node._get_name_nodes() + + def _get_return_nodes_skip_functions(self): + yield from () + + def _get_yield_nodes_skip_lambdas(self): + yield from () + + def _infer_name(self, frame, name): + # overridden for ImportFrom, Import, Global, TryExcept and Arguments + pass + + def _infer(self, context=None): + """we don't know how to resolve a statement by default""" + # this method is overridden by most concrete classes + raise exceptions.InferenceError( + "No inference function for {node!r}.", node=self, context=context + ) + + def inferred(self): + """Get a list of the inferred values. + + .. seealso:: :ref:`inference` + + :returns: The inferred values. + :rtype: list + """ + return list(self.infer()) + + def instantiate_class(self): + """Instantiate an instance of the defined class. + + .. note:: + + On anything other than a :class:`ClassDef` this will return self. + + :returns: An instance of the defined class. + :rtype: object + """ + return self + + def has_base(self, node): + """Check if this node inherits from the given type. + + :param node: The node defining the base to look for. + Usually this is a :class:`Name` node. + :type node: NodeNG + """ + return False + + def callable(self): + """Whether this node defines something that is callable. + + :returns: True if this defines something that is callable, + False otherwise. + :rtype: bool + """ + return False + + def eq(self, value): + return False + + def as_string(self): + """Get the source code that this node represents. + + :returns: The source code. + :rtype: str + """ + return as_string.to_code(self) + + def repr_tree( + self, + ids=False, + include_linenos=False, + ast_state=False, + indent=" ", + max_depth=0, + max_width=80, + ): + """Get a string representation of the AST from this node. + + :param ids: If true, includes the ids with the node type names. + :type ids: bool + + :param include_linenos: If true, includes the line numbers and + column offsets. + :type include_linenos: bool + + :param ast_state: If true, includes information derived from + the whole AST like local and global variables. + :type ast_state: bool + + :param indent: A string to use to indent the output string. + :type indent: str + + :param max_depth: If set to a positive integer, won't return + nodes deeper than max_depth in the string. + :type max_depth: int + + :param max_width: Attempt to format the output string to stay + within this number of characters, but can exceed it under some + circumstances. Only positive integer values are valid, the default is 80. + :type max_width: int + + :returns: The string representation of the AST. + :rtype: str + """ + # pylint: disable=too-many-statements + @_singledispatch + def _repr_tree(node, result, done, cur_indent="", depth=1): + """Outputs a representation of a non-tuple/list, non-node that's + contained within an AST, including strings. + """ + lines = pprint.pformat( + node, width=max(max_width - len(cur_indent), 1) + ).splitlines(True) + result.append(lines[0]) + result.extend([cur_indent + line for line in lines[1:]]) + return len(lines) != 1 + + # pylint: disable=unused-variable; doesn't understand singledispatch + @_repr_tree.register(tuple) + @_repr_tree.register(list) + def _repr_seq(node, result, done, cur_indent="", depth=1): + """Outputs a representation of a sequence that's contained within an AST.""" + cur_indent += indent + result.append("[") + if not node: + broken = False + elif len(node) == 1: + broken = _repr_tree(node[0], result, done, cur_indent, depth) + elif len(node) == 2: + broken = _repr_tree(node[0], result, done, cur_indent, depth) + if not broken: + result.append(", ") + else: + result.append(",\n") + result.append(cur_indent) + broken = _repr_tree(node[1], result, done, cur_indent, depth) or broken + else: + result.append("\n") + result.append(cur_indent) + for child in node[:-1]: + _repr_tree(child, result, done, cur_indent, depth) + result.append(",\n") + result.append(cur_indent) + _repr_tree(node[-1], result, done, cur_indent, depth) + broken = True + result.append("]") + return broken + + # pylint: disable=unused-variable; doesn't understand singledispatch + @_repr_tree.register(NodeNG) + def _repr_node(node, result, done, cur_indent="", depth=1): + """Outputs a strings representation of an astroid node.""" + if node in done: + result.append( + indent + + " max_depth: + result.append("...") + return False + depth += 1 + cur_indent += indent + if ids: + result.append("%s<0x%x>(\n" % (type(node).__name__, id(node))) + else: + result.append("%s(" % type(node).__name__) + fields = [] + if include_linenos: + fields.extend(("lineno", "col_offset")) + fields.extend(node._other_fields) + fields.extend(node._astroid_fields) + if ast_state: + fields.extend(node._other_other_fields) + if not fields: + broken = False + elif len(fields) == 1: + result.append("%s=" % fields[0]) + broken = _repr_tree( + getattr(node, fields[0]), result, done, cur_indent, depth + ) + else: + result.append("\n") + result.append(cur_indent) + for field in fields[:-1]: + result.append("%s=" % field) + _repr_tree(getattr(node, field), result, done, cur_indent, depth) + result.append(",\n") + result.append(cur_indent) + result.append("%s=" % fields[-1]) + _repr_tree(getattr(node, fields[-1]), result, done, cur_indent, depth) + broken = True + result.append(")") + return broken + + result = [] + _repr_tree(self, result, set()) + return "".join(result) + + def bool_value(self, context=None): + """Determine the boolean value of this node. + + The boolean value of a node can have three + possible values: + + * False: For instance, empty data structures, + False, empty strings, instances which return + explicitly False from the __nonzero__ / __bool__ + method. + * True: Most of constructs are True by default: + classes, functions, modules etc + * Uninferable: The inference engine is uncertain of the + node's value. + + :returns: The boolean value of this node. + :rtype: bool or Uninferable + """ + return util.Uninferable + + def op_precedence(self): + # Look up by class name or default to highest precedence + return OP_PRECEDENCE.get(self.__class__.__name__, len(OP_PRECEDENCE)) + + def op_left_associative(self): + # Everything is left associative except `**` and IfExp + return True + + +class Statement(NodeNG): + """Statement node adding a few attributes""" + + is_statement = True + """Whether this node indicates a statement. + + :type: bool + """ + + def next_sibling(self): + """The next sibling statement node. + + :returns: The next sibling statement node. + :rtype: NodeNG or None + """ + stmts = self.parent.child_sequence(self) + index = stmts.index(self) + try: + return stmts[index + 1] + except IndexError: + pass + + def previous_sibling(self): + """The previous sibling statement. + + :returns: The previous sibling statement node. + :rtype: NodeNG or None + """ + stmts = self.parent.child_sequence(self) + index = stmts.index(self) + if index >= 1: + return stmts[index - 1] + return None + + +class _BaseContainer( + mixins.ParentAssignTypeMixin, NodeNG, bases.Instance, metaclass=abc.ABCMeta +): + """Base class for Set, FrozenSet, Tuple and List.""" + + _astroid_fields = ("elts",) + + def __init__(self, lineno=None, col_offset=None, parent=None): + """ + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.elts = [] + """The elements in the node. + + :type: list(NodeNG) + """ + + super().__init__(lineno, col_offset, parent) + + def postinit(self, elts): + """Do some setup after initialisation. + + :param elts: The list of elements the that node contains. + :type elts: list(NodeNG) + """ + self.elts = elts + + @classmethod + def from_elements(cls, elts=None): + """Create a node of this type from the given list of elements. + + :param elts: The list of elements that the node should contain. + :type elts: list(NodeNG) + + :returns: A new node containing the given elements. + :rtype: NodeNG + """ + node = cls() + if elts is None: + node.elts = [] + else: + node.elts = [const_factory(e) if _is_const(e) else e for e in elts] + return node + + def itered(self): + """An iterator over the elements this node contains. + + :returns: The contents of this node. + :rtype: iterable(NodeNG) + """ + return self.elts + + def bool_value(self, context=None): + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + :rtype: bool or Uninferable + """ + return bool(self.elts) + + @abc.abstractmethod + def pytype(self): + """Get the name of the type that this node represents. + + :returns: The name of the type. + :rtype: str + """ + + def get_children(self): + yield from self.elts + + +class LookupMixIn: + """Mixin to look up a name in the right scope.""" + + @lru_cache(maxsize=None) + def lookup(self, name): + """Lookup where the given variable is assigned. + + The lookup starts from self's scope. If self is not a frame itself + and the name is found in the inner frame locals, statements will be + filtered to remove ignorable statements according to self's location. + + :param name: The name of the variable to find assignments for. + :type name: str + + :returns: The scope node and the list of assignments associated to the + given name according to the scope where it has been found (locals, + globals or builtin). + :rtype: tuple(str, list(NodeNG)) + """ + return self.scope().scope_lookup(self, name) + + def ilookup(self, name): + """Lookup the inferred values of the given variable. + + :param name: The variable name to find values for. + :type name: str + + :returns: The inferred values of the statements returned from + :meth:`lookup`. + :rtype: iterable + """ + frame, stmts = self.lookup(name) + context = contextmod.InferenceContext() + return bases._infer_stmts(stmts, context, frame) + + def _get_filtered_node_statements(self, nodes): + statements = [(node, node.statement()) for node in nodes] + # Next we check if we have ExceptHandlers that are parent + # of the underlying variable, in which case the last one survives + if len(statements) > 1 and all( + isinstance(stmt, ExceptHandler) for _, stmt in statements + ): + statements = [ + (node, stmt) for node, stmt in statements if stmt.parent_of(self) + ] + return statements + + def _filter_stmts(self, stmts, frame, offset): + """Filter the given list of statements to remove ignorable statements. + + If self is not a frame itself and the name is found in the inner + frame locals, statements will be filtered to remove ignorable + statements according to self's location. + + :param stmts: The statements to filter. + :type stmts: list(NodeNG) + + :param frame: The frame that all of the given statements belong to. + :type frame: NodeNG + + :param offset: The line offset to filter statements up to. + :type offset: int + + :returns: The filtered statements. + :rtype: list(NodeNG) + """ + # if offset == -1, my actual frame is not the inner frame but its parent + # + # class A(B): pass + # + # we need this to resolve B correctly + if offset == -1: + myframe = self.frame().parent.frame() + else: + myframe = self.frame() + # If the frame of this node is the same as the statement + # of this node, then the node is part of a class or + # a function definition and the frame of this node should be the + # the upper frame, not the frame of the definition. + # For more information why this is important, + # see Pylint issue #295. + # For example, for 'b', the statement is the same + # as the frame / scope: + # + # def test(b=1): + # ... + + if self.statement() is myframe and myframe.parent: + myframe = myframe.parent.frame() + mystmt = self.statement() + # line filtering if we are in the same frame + # + # take care node may be missing lineno information (this is the case for + # nodes inserted for living objects) + if myframe is frame and mystmt.fromlineno is not None: + assert mystmt.fromlineno is not None, mystmt + mylineno = mystmt.fromlineno + offset + else: + # disabling lineno filtering + mylineno = 0 + + _stmts = [] + _stmt_parents = [] + statements = self._get_filtered_node_statements(stmts) + + for node, stmt in statements: + # line filtering is on and we have reached our location, break + if stmt.fromlineno > mylineno > 0: + break + # Ignore decorators with the same name as the + # decorated function + # Fixes issue #375 + if mystmt is stmt and is_from_decorator(self): + continue + assert hasattr(node, "assign_type"), ( + node, + node.scope(), + node.scope().locals, + ) + assign_type = node.assign_type() + if node.has_base(self): + break + + _stmts, done = assign_type._get_filtered_stmts(self, node, _stmts, mystmt) + if done: + break + + optional_assign = assign_type.optional_assign + if optional_assign and assign_type.parent_of(self): + # we are inside a loop, loop var assignment is hiding previous + # assignment + _stmts = [node] + _stmt_parents = [stmt.parent] + continue + + if isinstance(assign_type, NamedExpr): + _stmts = [node] + continue + + # XXX comment various branches below!!! + try: + pindex = _stmt_parents.index(stmt.parent) + except ValueError: + pass + else: + # we got a parent index, this means the currently visited node + # is at the same block level as a previously visited node + if _stmts[pindex].assign_type().parent_of(assign_type): + # both statements are not at the same block level + continue + # if currently visited node is following previously considered + # assignment and both are not exclusive, we can drop the + # previous one. For instance in the following code :: + # + # if a: + # x = 1 + # else: + # x = 2 + # print x + # + # we can't remove neither x = 1 nor x = 2 when looking for 'x' + # of 'print x'; while in the following :: + # + # x = 1 + # x = 2 + # print x + # + # we can remove x = 1 when we see x = 2 + # + # moreover, on loop assignment types, assignment won't + # necessarily be done if the loop has no iteration, so we don't + # want to clear previous assignments if any (hence the test on + # optional_assign) + if not (optional_assign or are_exclusive(_stmts[pindex], node)): + if ( + # In case of partial function node, if the statement is different + # from the origin function then it can be deleted otherwise it should + # remain to be able to correctly infer the call to origin function. + not node.is_function + or node.qname() != "PartialFunction" + or node.name != _stmts[pindex].name + ): + del _stmt_parents[pindex] + del _stmts[pindex] + if isinstance(node, AssignName): + if not optional_assign and stmt.parent is mystmt.parent: + _stmts = [] + _stmt_parents = [] + elif isinstance(node, DelName): + _stmts = [] + _stmt_parents = [] + continue + if not are_exclusive(self, node): + _stmts.append(node) + _stmt_parents.append(stmt.parent) + return _stmts + + +# Name classes + + +class AssignName( + mixins.NoChildrenMixin, LookupMixIn, mixins.ParentAssignTypeMixin, NodeNG +): + """Variation of :class:`ast.Assign` representing assignment to a name. + + An :class:`AssignName` is the name of something that is assigned to. + This includes variables defined in a function signature or in a loop. + + >>> node = astroid.extract_node('variable = range(10)') + >>> node + + >>> list(node.get_children()) + [, ] + >>> list(node.get_children())[0].as_string() + 'variable' + """ + + _other_fields = ("name",) + + def __init__(self, name=None, lineno=None, col_offset=None, parent=None): + """ + :param name: The name that is assigned to. + :type name: str or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.name = name + """The name that is assigned to. + + :type: str or None + """ + + super().__init__(lineno, col_offset, parent) + + +class DelName( + mixins.NoChildrenMixin, LookupMixIn, mixins.ParentAssignTypeMixin, NodeNG +): + """Variation of :class:`ast.Delete` representing deletion of a name. + + A :class:`DelName` is the name of something that is deleted. + + >>> node = astroid.extract_node("del variable #@") + >>> list(node.get_children()) + [] + >>> list(node.get_children())[0].as_string() + 'variable' + """ + + _other_fields = ("name",) + + def __init__(self, name=None, lineno=None, col_offset=None, parent=None): + """ + :param name: The name that is being deleted. + :type name: str or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.name = name + """The name that is being deleted. + + :type: str or None + """ + + super().__init__(lineno, col_offset, parent) + + +class Name(mixins.NoChildrenMixin, LookupMixIn, NodeNG): + """Class representing an :class:`ast.Name` node. + + A :class:`Name` node is something that is named, but not covered by + :class:`AssignName` or :class:`DelName`. + + >>> node = astroid.extract_node('range(10)') + >>> node + + >>> list(node.get_children()) + [, ] + >>> list(node.get_children())[0].as_string() + 'range' + """ + + _other_fields = ("name",) + + def __init__(self, name=None, lineno=None, col_offset=None, parent=None): + """ + :param name: The name that this node refers to. + :type name: str or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.name = name + """The name that this node refers to. + + :type: str or None + """ + + super().__init__(lineno, col_offset, parent) + + def _get_name_nodes(self): + yield self + + for child_node in self.get_children(): + yield from child_node._get_name_nodes() + + +class Arguments(mixins.AssignTypeMixin, NodeNG): + """Class representing an :class:`ast.arguments` node. + + An :class:`Arguments` node represents that arguments in a + function definition. + + >>> node = astroid.extract_node('def foo(bar): pass') + >>> node + + >>> node.args + + """ + + # Python 3.4+ uses a different approach regarding annotations, + # each argument is a new class, _ast.arg, which exposes an + # 'annotation' attribute. In astroid though, arguments are exposed + # as is in the Arguments node and the only way to expose annotations + # is by using something similar with Python 3.3: + # - we expose 'varargannotation' and 'kwargannotation' of annotations + # of varargs and kwargs. + # - we expose 'annotation', a list with annotations for + # for each normal argument. If an argument doesn't have an + # annotation, its value will be None. + # pylint: disable=too-many-instance-attributes + _astroid_fields = ( + "args", + "defaults", + "kwonlyargs", + "posonlyargs", + "posonlyargs_annotations", + "kw_defaults", + "annotations", + "varargannotation", + "kwargannotation", + "kwonlyargs_annotations", + "type_comment_args", + "type_comment_kwonlyargs", + "type_comment_posonlyargs", + ) + varargannotation = None + """The type annotation for the variable length arguments. + + :type: NodeNG + """ + kwargannotation = None + """The type annotation for the variable length keyword arguments. + + :type: NodeNG + """ + + _other_fields = ("vararg", "kwarg") + + def __init__(self, vararg=None, kwarg=None, parent=None): + """ + :param vararg: The name of the variable length arguments. + :type vararg: str or None + + :param kwarg: The name of the variable length keyword arguments. + :type kwarg: str or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + super().__init__(parent=parent) + self.vararg = vararg + """The name of the variable length arguments. + + :type: str or None + """ + + self.kwarg = kwarg + """The name of the variable length keyword arguments. + + :type: str or None + """ + + self.args = [] + """The names of the required arguments. + + :type: list(AssignName) + """ + + self.defaults = [] + """The default values for arguments that can be passed positionally. + + :type: list(NodeNG) + """ + + self.kwonlyargs = [] + """The keyword arguments that cannot be passed positionally. + + :type: list(AssignName) + """ + + self.posonlyargs = [] + """The arguments that can only be passed positionally. + + :type: list(AssignName) + """ + + self.kw_defaults = [] + """The default values for keyword arguments that cannot be passed positionally. + + :type: list(NodeNG) + """ + + self.annotations = [] + """The type annotations of arguments that can be passed positionally. + + :type: list(NodeNG) + """ + + self.posonlyargs_annotations = [] + """The type annotations of arguments that can only be passed positionally. + + :type: list(NodeNG) + """ + + self.kwonlyargs_annotations = [] + """The type annotations of arguments that cannot be passed positionally. + + :type: list(NodeNG) + """ + + self.type_comment_args = [] + """The type annotation, passed by a type comment, of each argument. + + If an argument does not have a type comment, + the value for that argument will be None. + + :type: list(NodeNG or None) + """ + + self.type_comment_kwonlyargs = [] + """The type annotation, passed by a type comment, of each keyword only argument. + + If an argument does not have a type comment, + the value for that argument will be None. + + :type: list(NodeNG or None) + """ + + self.type_comment_posonlyargs = [] + """The type annotation, passed by a type comment, of each positional argument. + + If an argument does not have a type comment, + the value for that argument will be None. + + :type: list(NodeNG or None) + """ + + # pylint: disable=too-many-arguments + def postinit( + self, + args, + defaults, + kwonlyargs, + kw_defaults, + annotations, + posonlyargs=None, + kwonlyargs_annotations=None, + posonlyargs_annotations=None, + varargannotation=None, + kwargannotation=None, + type_comment_args=None, + type_comment_kwonlyargs=None, + type_comment_posonlyargs=None, + ): + """Do some setup after initialisation. + + :param args: The names of the required arguments. + :type args: list(AssignName) + + :param defaults: The default values for arguments that can be passed + positionally. + :type defaults: list(NodeNG) + + :param kwonlyargs: The keyword arguments that cannot be passed + positionally. + :type kwonlyargs: list(AssignName) + + :param posonlyargs: The arguments that can only be passed + positionally. + :type kwonlyargs: list(AssignName) + + :param kw_defaults: The default values for keyword arguments that + cannot be passed positionally. + :type kw_defaults: list(NodeNG) + + :param annotations: The type annotations of arguments that can be + passed positionally. + :type annotations: list(NodeNG) + + :param kwonlyargs_annotations: The type annotations of arguments that + cannot be passed positionally. This should always be passed in + Python 3. + :type kwonlyargs_annotations: list(NodeNG) + + :param posonlyargs_annotations: The type annotations of arguments that + can only be passed positionally. This should always be passed in + Python 3. + :type posonlyargs_annotations: list(NodeNG) + + :param varargannotation: The type annotation for the variable length + arguments. + :type varargannotation: NodeNG + + :param kwargannotation: The type annotation for the variable length + keyword arguments. + :type kwargannotation: NodeNG + + :param type_comment_args: The type annotation, + passed by a type comment, of each argument. + :type type_comment_args: list(NodeNG or None) + + :param type_comment_args: The type annotation, + passed by a type comment, of each keyword only argument. + :type type_comment_args: list(NodeNG or None) + + :param type_comment_args: The type annotation, + passed by a type comment, of each positional argument. + :type type_comment_args: list(NodeNG or None) + """ + self.args = args + self.defaults = defaults + self.kwonlyargs = kwonlyargs + self.posonlyargs = posonlyargs + self.kw_defaults = kw_defaults + self.annotations = annotations + self.kwonlyargs_annotations = kwonlyargs_annotations + self.posonlyargs_annotations = posonlyargs_annotations + self.varargannotation = varargannotation + self.kwargannotation = kwargannotation + self.type_comment_args = type_comment_args + self.type_comment_kwonlyargs = type_comment_kwonlyargs + self.type_comment_posonlyargs = type_comment_posonlyargs + + # pylint: disable=too-many-arguments + + def _infer_name(self, frame, name): + if self.parent is frame: + return name + return None + + @decorators.cachedproperty + def fromlineno(self): + """The first line that this node appears on in the source code. + + :type: int or None + """ + lineno = super().fromlineno + return max(lineno, self.parent.fromlineno or 0) + + @decorators.cachedproperty + def arguments(self): + """Get all the arguments for this node, including positional only and positional and keyword""" + return list(itertools.chain((self.posonlyargs or ()), self.args or ())) + + def format_args(self): + """Get the arguments formatted as string. + + :returns: The formatted arguments. + :rtype: str + """ + result = [] + positional_only_defaults = [] + positional_or_keyword_defaults = self.defaults + if self.defaults: + args = self.args or [] + positional_or_keyword_defaults = self.defaults[-len(args) :] + positional_only_defaults = self.defaults[: len(self.defaults) - len(args)] + + if self.posonlyargs: + result.append( + _format_args( + self.posonlyargs, + positional_only_defaults, + self.posonlyargs_annotations, + ) + ) + result.append("/") + if self.args: + result.append( + _format_args( + self.args, + positional_or_keyword_defaults, + getattr(self, "annotations", None), + ) + ) + if self.vararg: + result.append("*%s" % self.vararg) + if self.kwonlyargs: + if not self.vararg: + result.append("*") + result.append( + _format_args( + self.kwonlyargs, self.kw_defaults, self.kwonlyargs_annotations + ) + ) + if self.kwarg: + result.append("**%s" % self.kwarg) + return ", ".join(result) + + def default_value(self, argname): + """Get the default value for an argument. + + :param argname: The name of the argument to get the default value for. + :type argname: str + + :raises NoDefault: If there is no default value defined for the + given argument. + """ + args = self.arguments + index = _find_arg(argname, args)[0] + if index is not None: + idx = index - (len(args) - len(self.defaults)) + if idx >= 0: + return self.defaults[idx] + index = _find_arg(argname, self.kwonlyargs)[0] + if index is not None and self.kw_defaults[index] is not None: + return self.kw_defaults[index] + raise exceptions.NoDefault(func=self.parent, name=argname) + + def is_argument(self, name): + """Check if the given name is defined in the arguments. + + :param name: The name to check for. + :type name: str + + :returns: True if the given name is defined in the arguments, + False otherwise. + :rtype: bool + """ + if name == self.vararg: + return True + if name == self.kwarg: + return True + return ( + self.find_argname(name, rec=True)[1] is not None + or self.kwonlyargs + and _find_arg(name, self.kwonlyargs, rec=True)[1] is not None + ) + + def find_argname(self, argname, rec=False): + """Get the index and :class:`AssignName` node for given name. + + :param argname: The name of the argument to search for. + :type argname: str + + :param rec: Whether or not to include arguments in unpacked tuples + in the search. + :type rec: bool + + :returns: The index and node for the argument. + :rtype: tuple(str or None, AssignName or None) + """ + if self.arguments: + return _find_arg(argname, self.arguments, rec) + return None, None + + def get_children(self): + yield from self.posonlyargs or () + + for elt in self.posonlyargs_annotations: + if elt is not None: + yield elt + + yield from self.args or () + + yield from self.defaults + yield from self.kwonlyargs + + for elt in self.kw_defaults: + if elt is not None: + yield elt + + for elt in self.annotations: + if elt is not None: + yield elt + + if self.varargannotation is not None: + yield self.varargannotation + + if self.kwargannotation is not None: + yield self.kwargannotation + + for elt in self.kwonlyargs_annotations: + if elt is not None: + yield elt + + +def _find_arg(argname, args, rec=False): + for i, arg in enumerate(args): + if isinstance(arg, Tuple): + if rec: + found = _find_arg(argname, arg.elts) + if found[0] is not None: + return found + elif arg.name == argname: + return i, arg + return None, None + + +def _format_args(args, defaults=None, annotations=None): + values = [] + if args is None: + return "" + if annotations is None: + annotations = [] + if defaults is not None: + default_offset = len(args) - len(defaults) + packed = itertools.zip_longest(args, annotations) + for i, (arg, annotation) in enumerate(packed): + if isinstance(arg, Tuple): + values.append("(%s)" % _format_args(arg.elts)) + else: + argname = arg.name + default_sep = "=" + if annotation is not None: + argname += ": " + annotation.as_string() + default_sep = " = " + values.append(argname) + + if defaults is not None and i >= default_offset: + if defaults[i - default_offset] is not None: + values[-1] += default_sep + defaults[i - default_offset].as_string() + return ", ".join(values) + + +class AssignAttr(mixins.ParentAssignTypeMixin, NodeNG): + """Variation of :class:`ast.Assign` representing assignment to an attribute. + + >>> node = astroid.extract_node('self.attribute = range(10)') + >>> node + + >>> list(node.get_children()) + [, ] + >>> list(node.get_children())[0].as_string() + 'self.attribute' + """ + + _astroid_fields = ("expr",) + _other_fields = ("attrname",) + expr = None + """What has the attribute that is being assigned to. + + :type: NodeNG or None + """ + + def __init__(self, attrname=None, lineno=None, col_offset=None, parent=None): + """ + :param attrname: The name of the attribute being assigned to. + :type attrname: str or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.attrname = attrname + """The name of the attribute being assigned to. + + :type: str or None + """ + + super().__init__(lineno, col_offset, parent) + + def postinit(self, expr=None): + """Do some setup after initialisation. + + :param expr: What has the attribute that is being assigned to. + :type expr: NodeNG or None + """ + self.expr = expr + + def get_children(self): + yield self.expr + + +class Assert(Statement): + """Class representing an :class:`ast.Assert` node. + + An :class:`Assert` node represents an assert statement. + + >>> node = astroid.extract_node('assert len(things) == 10, "Not enough things"') + >>> node + + """ + + _astroid_fields = ("test", "fail") + test = None + """The test that passes or fails the assertion. + + :type: NodeNG or None + """ + fail = None + """The message shown when the assertion fails. + + :type: NodeNG or None + """ + + def postinit(self, test=None, fail=None): + """Do some setup after initialisation. + + :param test: The test that passes or fails the assertion. + :type test: NodeNG or None + + :param fail: The message shown when the assertion fails. + :type fail: NodeNG or None + """ + self.fail = fail + self.test = test + + def get_children(self): + yield self.test + + if self.fail is not None: + yield self.fail + + +class Assign(mixins.AssignTypeMixin, Statement): + """Class representing an :class:`ast.Assign` node. + + An :class:`Assign` is a statement where something is explicitly + asssigned to. + + >>> node = astroid.extract_node('variable = range(10)') + >>> node + + """ + + _astroid_fields = ("targets", "value") + _other_other_fields = ("type_annotation",) + targets = None + """What is being assigned to. + + :type: list(NodeNG) or None + """ + value = None + """The value being assigned to the variables. + + :type: NodeNG or None + """ + type_annotation = None + """If present, this will contain the type annotation passed by a type comment + + :type: NodeNG or None + """ + + def postinit(self, targets=None, value=None, type_annotation=None): + """Do some setup after initialisation. + + :param targets: What is being assigned to. + :type targets: list(NodeNG) or None + + :param value: The value being assigned to the variables. + :type: NodeNG or None + """ + self.targets = targets + self.value = value + self.type_annotation = type_annotation + + def get_children(self): + yield from self.targets + + yield self.value + + @decorators.cached + def _get_assign_nodes(self): + return [self] + list(self.value._get_assign_nodes()) + + def _get_yield_nodes_skip_lambdas(self): + yield from self.value._get_yield_nodes_skip_lambdas() + + +class AnnAssign(mixins.AssignTypeMixin, Statement): + """Class representing an :class:`ast.AnnAssign` node. + + An :class:`AnnAssign` is an assignment with a type annotation. + + >>> node = astroid.extract_node('variable: List[int] = range(10)') + >>> node + + """ + + _astroid_fields = ("target", "annotation", "value") + _other_fields = ("simple",) + target = None + """What is being assigned to. + + :type: NodeNG or None + """ + annotation = None + """The type annotation of what is being assigned to. + + :type: NodeNG + """ + value = None + """The value being assigned to the variables. + + :type: NodeNG or None + """ + simple = None + """Whether :attr:`target` is a pure name or a complex statement. + + :type: int + """ + + def postinit(self, target, annotation, simple, value=None): + """Do some setup after initialisation. + + :param target: What is being assigned to. + :type target: NodeNG + + :param annotation: The type annotation of what is being assigned to. + :type: NodeNG + + :param simple: Whether :attr:`target` is a pure name + or a complex statement. + :type simple: int + + :param value: The value being assigned to the variables. + :type: NodeNG or None + """ + self.target = target + self.annotation = annotation + self.value = value + self.simple = simple + + def get_children(self): + yield self.target + yield self.annotation + + if self.value is not None: + yield self.value + + +class AugAssign(mixins.AssignTypeMixin, Statement): + """Class representing an :class:`ast.AugAssign` node. + + An :class:`AugAssign` is an assignment paired with an operator. + + >>> node = astroid.extract_node('variable += 1') + >>> node + + """ + + _astroid_fields = ("target", "value") + _other_fields = ("op",) + target = None + """What is being assigned to. + + :type: NodeNG or None + """ + value = None + """The value being assigned to the variable. + + :type: NodeNG or None + """ + + def __init__(self, op=None, lineno=None, col_offset=None, parent=None): + """ + :param op: The operator that is being combined with the assignment. + This includes the equals sign. + :type op: str or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.op = op + """The operator that is being combined with the assignment. + + This includes the equals sign. + + :type: str or None + """ + + super().__init__(lineno, col_offset, parent) + + def postinit(self, target=None, value=None): + """Do some setup after initialisation. + + :param target: What is being assigned to. + :type target: NodeNG or None + + :param value: The value being assigned to the variable. + :type: NodeNG or None + """ + self.target = target + self.value = value + + # This is set by inference.py + def _infer_augassign(self, context=None): + raise NotImplementedError + + def type_errors(self, context=None): + """Get a list of type errors which can occur during inference. + + Each TypeError is represented by a :class:`BadBinaryOperationMessage` , + which holds the original exception. + + :returns: The list of possible type errors. + :rtype: list(BadBinaryOperationMessage) + """ + try: + results = self._infer_augassign(context=context) + return [ + result + for result in results + if isinstance(result, util.BadBinaryOperationMessage) + ] + except exceptions.InferenceError: + return [] + + def get_children(self): + yield self.target + yield self.value + + +class Repr(NodeNG): + """Class representing an :class:`ast.Repr` node. + + A :class:`Repr` node represents the backtick syntax, + which is a deprecated alias for :func:`repr` removed in Python 3. + + >>> node = astroid.extract_node('`variable`') + >>> node + + """ + + _astroid_fields = ("value",) + value = None + """What is having :func:`repr` called on it. + + :type: NodeNG or None + """ + + def postinit(self, value=None): + """Do some setup after initialisation. + + :param value: What is having :func:`repr` called on it. + :type value: NodeNG or None + """ + self.value = value + + +class BinOp(NodeNG): + """Class representing an :class:`ast.BinOp` node. + + A :class:`BinOp` node is an application of a binary operator. + + >>> node = astroid.extract_node('a + b') + >>> node + + """ + + _astroid_fields = ("left", "right") + _other_fields = ("op",) + left = None + """What is being applied to the operator on the left side. + + :type: NodeNG or None + """ + right = None + """What is being applied to the operator on the right side. + + :type: NodeNG or None + """ + + def __init__(self, op=None, lineno=None, col_offset=None, parent=None): + """ + :param op: The operator. + :type: str or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.op = op + """The operator. + + :type: str or None + """ + + super().__init__(lineno, col_offset, parent) + + def postinit(self, left=None, right=None): + """Do some setup after initialisation. + + :param left: What is being applied to the operator on the left side. + :type left: NodeNG or None + + :param right: What is being applied to the operator on the right side. + :type right: NodeNG or None + """ + self.left = left + self.right = right + + # This is set by inference.py + def _infer_binop(self, context=None): + raise NotImplementedError + + def type_errors(self, context=None): + """Get a list of type errors which can occur during inference. + + Each TypeError is represented by a :class:`BadBinaryOperationMessage`, + which holds the original exception. + + :returns: The list of possible type errors. + :rtype: list(BadBinaryOperationMessage) + """ + try: + results = self._infer_binop(context=context) + return [ + result + for result in results + if isinstance(result, util.BadBinaryOperationMessage) + ] + except exceptions.InferenceError: + return [] + + def get_children(self): + yield self.left + yield self.right + + def op_precedence(self): + return OP_PRECEDENCE[self.op] + + def op_left_associative(self): + # 2**3**4 == 2**(3**4) + return self.op != "**" + + +class BoolOp(NodeNG): + """Class representing an :class:`ast.BoolOp` node. + + A :class:`BoolOp` is an application of a boolean operator. + + >>> node = astroid.extract_node('a and b') + >>> node + + """ + + _astroid_fields = ("values",) + _other_fields = ("op",) + values = None + """The values being applied to the operator. + + :type: list(NodeNG) or None + """ + + def __init__(self, op=None, lineno=None, col_offset=None, parent=None): + """ + :param op: The operator. + :type: str or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.op = op + """The operator. + + :type: str or None + """ + + super().__init__(lineno, col_offset, parent) + + def postinit(self, values=None): + """Do some setup after initialisation. + + :param values: The values being applied to the operator. + :type values: list(NodeNG) or None + """ + self.values = values + + def get_children(self): + yield from self.values + + def op_precedence(self): + return OP_PRECEDENCE[self.op] + + +class Break(mixins.NoChildrenMixin, Statement): + """Class representing an :class:`ast.Break` node. + + >>> node = astroid.extract_node('break') + >>> node + + """ + + +class Call(NodeNG): + """Class representing an :class:`ast.Call` node. + + A :class:`Call` node is a call to a function, method, etc. + + >>> node = astroid.extract_node('function()') + >>> node + + """ + + _astroid_fields = ("func", "args", "keywords") + func = None + """What is being called. + + :type: NodeNG or None + """ + args = None + """The positional arguments being given to the call. + + :type: list(NodeNG) or None + """ + keywords = None + """The keyword arguments being given to the call. + + :type: list(NodeNG) or None + """ + + def postinit(self, func=None, args=None, keywords=None): + """Do some setup after initialisation. + + :param func: What is being called. + :type func: NodeNG or None + + :param args: The positional arguments being given to the call. + :type args: list(NodeNG) or None + + :param keywords: The keyword arguments being given to the call. + :type keywords: list(NodeNG) or None + """ + self.func = func + self.args = args + self.keywords = keywords + + @property + def starargs(self): + """The positional arguments that unpack something. + + :type: list(Starred) + """ + args = self.args or [] + return [arg for arg in args if isinstance(arg, Starred)] + + @property + def kwargs(self): + """The keyword arguments that unpack something. + + :type: list(Keyword) + """ + keywords = self.keywords or [] + return [keyword for keyword in keywords if keyword.arg is None] + + def get_children(self): + yield self.func + + yield from self.args + + yield from self.keywords or () + + +class Compare(NodeNG): + """Class representing an :class:`ast.Compare` node. + + A :class:`Compare` node indicates a comparison. + + >>> node = astroid.extract_node('a <= b <= c') + >>> node + + >>> node.ops + [('<=', ), ('<=', )] + """ + + _astroid_fields = ("left", "ops") + left = None + """The value at the left being applied to a comparison operator. + + :type: NodeNG or None + """ + ops = None + """The remainder of the operators and their relevant right hand value. + + :type: list(tuple(str, NodeNG)) or None + """ + + def postinit(self, left=None, ops=None): + """Do some setup after initialisation. + + :param left: The value at the left being applied to a comparison + operator. + :type left: NodeNG or None + + :param ops: The remainder of the operators + and their relevant right hand value. + :type ops: list(tuple(str, NodeNG)) or None + """ + self.left = left + self.ops = ops + + def get_children(self): + """Get the child nodes below this node. + + Overridden to handle the tuple fields and skip returning the operator + strings. + + :returns: The children. + :rtype: iterable(NodeNG) + """ + yield self.left + for _, comparator in self.ops: + yield comparator # we don't want the 'op' + + def last_child(self): + """An optimized version of list(get_children())[-1] + + :returns: The last child. + :rtype: NodeNG + """ + # XXX maybe if self.ops: + return self.ops[-1][1] + # return self.left + + +class Comprehension(NodeNG): + """Class representing an :class:`ast.comprehension` node. + + A :class:`Comprehension` indicates the loop inside any type of + comprehension including generator expressions. + + >>> node = astroid.extract_node('[x for x in some_values]') + >>> list(node.get_children()) + [, ] + >>> list(node.get_children())[1].as_string() + 'for x in some_values' + """ + + _astroid_fields = ("target", "iter", "ifs") + _other_fields = ("is_async",) + target = None + """What is assigned to by the comprehension. + + :type: NodeNG or None + """ + iter = None + """What is iterated over by the comprehension. + + :type: NodeNG or None + """ + ifs = None + """The contents of any if statements that filter the comprehension. + + :type: list(NodeNG) or None + """ + is_async = None + """Whether this is an asynchronous comprehension or not. + + :type: bool or None + """ + + def __init__(self, parent=None): + """ + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + super().__init__() + self.parent = parent + + # pylint: disable=redefined-builtin; same name as builtin ast module. + def postinit(self, target=None, iter=None, ifs=None, is_async=None): + """Do some setup after initialisation. + + :param target: What is assigned to by the comprehension. + :type target: NodeNG or None + + :param iter: What is iterated over by the comprehension. + :type iter: NodeNG or None + + :param ifs: The contents of any if statements that filter + the comprehension. + :type ifs: list(NodeNG) or None + + :param is_async: Whether this is an asynchronous comprehension or not. + :type: bool or None + """ + self.target = target + self.iter = iter + self.ifs = ifs + self.is_async = is_async + + optional_assign = True + """Whether this node optionally assigns a variable. + + :type: bool + """ + + def assign_type(self): + """The type of assignment that this node performs. + + :returns: The assignment type. + :rtype: NodeNG + """ + return self + + def _get_filtered_stmts(self, lookup_node, node, stmts, mystmt): + """method used in filter_stmts""" + if self is mystmt: + if isinstance(lookup_node, (Const, Name)): + return [lookup_node], True + + elif self.statement() is mystmt: + # original node's statement is the assignment, only keeps + # current node (gen exp, list comp) + + return [node], True + + return stmts, False + + def get_children(self): + yield self.target + yield self.iter + + yield from self.ifs + + +class Const(mixins.NoChildrenMixin, NodeNG, bases.Instance): + """Class representing any constant including num, str, bool, None, bytes. + + >>> node = astroid.extract_node('(5, "This is a string.", True, None, b"bytes")') + >>> node + + >>> list(node.get_children()) + [, + , + , + , + ] + """ + + _other_fields = ("value",) + + def __init__(self, value, lineno=None, col_offset=None, parent=None): + """ + :param value: The value that the constant represents. + :type value: object + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.value = value + """The value that the constant represents. + + :type: object + """ + + super().__init__(lineno, col_offset, parent) + + def __getattr__(self, name): + # This is needed because of Proxy's __getattr__ method. + # Calling object.__new__ on this class without calling + # __init__ would result in an infinite loop otherwise + # since __getattr__ is called when an attribute doesn't + # exist and self._proxied indirectly calls self.value + # and Proxy __getattr__ calls self.value + if name == "value": + raise AttributeError + return super().__getattr__(name) + + def getitem(self, index, context=None): + """Get an item from this node if subscriptable. + + :param index: The node to use as a subscript index. + :type index: Const or Slice + + :raises AstroidTypeError: When the given index cannot be used as a + subscript index, or if this node is not subscriptable. + """ + if isinstance(index, Const): + index_value = index.value + elif isinstance(index, Slice): + index_value = _infer_slice(index, context=context) + + else: + raise exceptions.AstroidTypeError( + "Could not use type {} as subscript index".format(type(index)) + ) + + try: + if isinstance(self.value, (str, bytes)): + return Const(self.value[index_value]) + except IndexError as exc: + raise exceptions.AstroidIndexError( + message="Index {index!r} out of range", + node=self, + index=index, + context=context, + ) from exc + except TypeError as exc: + raise exceptions.AstroidTypeError( + message="Type error {error!r}", node=self, index=index, context=context + ) from exc + + raise exceptions.AstroidTypeError("%r (value=%s)" % (self, self.value)) + + def has_dynamic_getattr(self): + """Check if the node has a custom __getattr__ or __getattribute__. + + :returns: True if the class has a custom + __getattr__ or __getattribute__, False otherwise. + For a :class:`Const` this is always ``False``. + :rtype: bool + """ + return False + + def itered(self): + """An iterator over the elements this node contains. + + :returns: The contents of this node. + :rtype: iterable(Const) + + :raises TypeError: If this node does not represent something that is iterable. + """ + if isinstance(self.value, str): + return [const_factory(elem) for elem in self.value] + raise TypeError("Cannot iterate over type {!r}".format(type(self.value))) + + def pytype(self): + """Get the name of the type that this node represents. + + :returns: The name of the type. + :rtype: str + """ + return self._proxied.qname() + + def bool_value(self, context=None): + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + :rtype: bool + """ + return bool(self.value) + + +class Continue(mixins.NoChildrenMixin, Statement): + """Class representing an :class:`ast.Continue` node. + + >>> node = astroid.extract_node('continue') + >>> node + + """ + + +class Decorators(NodeNG): + """A node representing a list of decorators. + + A :class:`Decorators` is the decorators that are applied to + a method or function. + + >>> node = astroid.extract_node(''' + @property + def my_property(self): + return 3 + ''') + >>> node + + >>> list(node.get_children())[0] + + """ + + _astroid_fields = ("nodes",) + nodes = None + """The decorators that this node contains. + + :type: list(Name or Call) or None + """ + + def postinit(self, nodes): + """Do some setup after initialisation. + + :param nodes: The decorators that this node contains. + :type nodes: list(Name or Call) + """ + self.nodes = nodes + + def scope(self): + """The first parent node defining a new scope. + + :returns: The first parent scope node. + :rtype: Module or FunctionDef or ClassDef or Lambda or GenExpr + """ + # skip the function node to go directly to the upper level scope + return self.parent.parent.scope() + + def get_children(self): + yield from self.nodes + + +class DelAttr(mixins.ParentAssignTypeMixin, NodeNG): + """Variation of :class:`ast.Delete` representing deletion of an attribute. + + >>> node = astroid.extract_node('del self.attr') + >>> node + + >>> list(node.get_children())[0] + + """ + + _astroid_fields = ("expr",) + _other_fields = ("attrname",) + expr = None + """The name that this node represents. + + :type: Name or None + """ + + def __init__(self, attrname=None, lineno=None, col_offset=None, parent=None): + """ + :param attrname: The name of the attribute that is being deleted. + :type attrname: str or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.attrname = attrname + """The name of the attribute that is being deleted. + + :type: str or None + """ + + super().__init__(lineno, col_offset, parent) + + def postinit(self, expr=None): + """Do some setup after initialisation. + + :param expr: The name that this node represents. + :type expr: Name or None + """ + self.expr = expr + + def get_children(self): + yield self.expr + + +class Delete(mixins.AssignTypeMixin, Statement): + """Class representing an :class:`ast.Delete` node. + + A :class:`Delete` is a ``del`` statement this is deleting something. + + >>> node = astroid.extract_node('del self.attr') + >>> node + + """ + + _astroid_fields = ("targets",) + targets = None + """What is being deleted. + + :type: list(NodeNG) or None + """ + + def postinit(self, targets=None): + """Do some setup after initialisation. + + :param targets: What is being deleted. + :type targets: list(NodeNG) or None + """ + self.targets = targets + + def get_children(self): + yield from self.targets + + +class Dict(NodeNG, bases.Instance): + """Class representing an :class:`ast.Dict` node. + + A :class:`Dict` is a dictionary that is created with ``{}`` syntax. + + >>> node = astroid.extract_node('{1: "1"}') + >>> node + + """ + + _astroid_fields = ("items",) + + def __init__(self, lineno=None, col_offset=None, parent=None): + """ + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.items = [] + """The key-value pairs contained in the dictionary. + + :type: list(tuple(NodeNG, NodeNG)) + """ + + super().__init__(lineno, col_offset, parent) + + def postinit(self, items): + """Do some setup after initialisation. + + :param items: The key-value pairs contained in the dictionary. + :type items: list(tuple(NodeNG, NodeNG)) + """ + self.items = items + + @classmethod + def from_elements(cls, items=None): + """Create a :class:`Dict` of constants from a live dictionary. + + :param items: The items to store in the node. + :type items: dict + + :returns: The created dictionary node. + :rtype: Dict + """ + node = cls() + if items is None: + node.items = [] + else: + node.items = [ + (const_factory(k), const_factory(v) if _is_const(v) else v) + for k, v in items.items() + # The keys need to be constants + if _is_const(k) + ] + return node + + def pytype(self): + """Get the name of the type that this node represents. + + :returns: The name of the type. + :rtype: str + """ + return "%s.dict" % BUILTINS + + def get_children(self): + """Get the key and value nodes below this node. + + Children are returned in the order that they are defined in the source + code, key first then the value. + + :returns: The children. + :rtype: iterable(NodeNG) + """ + for key, value in self.items: + yield key + yield value + + def last_child(self): + """An optimized version of list(get_children())[-1] + + :returns: The last child, or None if no children exist. + :rtype: NodeNG or None + """ + if self.items: + return self.items[-1][1] + return None + + def itered(self): + """An iterator over the keys this node contains. + + :returns: The keys of this node. + :rtype: iterable(NodeNG) + """ + return [key for (key, _) in self.items] + + def getitem(self, index, context=None): + """Get an item from this node. + + :param index: The node to use as a subscript index. + :type index: Const or Slice + + :raises AstroidTypeError: When the given index cannot be used as a + subscript index, or if this node is not subscriptable. + :raises AstroidIndexError: If the given index does not exist in the + dictionary. + """ + for key, value in self.items: + # TODO(cpopa): no support for overriding yet, {1:2, **{1: 3}}. + if isinstance(key, DictUnpack): + try: + return value.getitem(index, context) + except (exceptions.AstroidTypeError, exceptions.AstroidIndexError): + continue + for inferredkey in key.infer(context): + if inferredkey is util.Uninferable: + continue + if isinstance(inferredkey, Const) and isinstance(index, Const): + if inferredkey.value == index.value: + return value + + raise exceptions.AstroidIndexError(index) + + def bool_value(self, context=None): + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + :rtype: bool + """ + return bool(self.items) + + +class Expr(Statement): + """Class representing an :class:`ast.Expr` node. + + An :class:`Expr` is any expression that does not have its value used or + stored. + + >>> node = astroid.extract_node('method()') + >>> node + + >>> node.parent + + """ + + _astroid_fields = ("value",) + value = None + """What the expression does. + + :type: NodeNG or None + """ + + def postinit(self, value=None): + """Do some setup after initialisation. + + :param value: What the expression does. + :type value: NodeNG or None + """ + self.value = value + + def get_children(self): + yield self.value + + def _get_yield_nodes_skip_lambdas(self): + if not self.value.is_lambda: + yield from self.value._get_yield_nodes_skip_lambdas() + + +class Ellipsis(mixins.NoChildrenMixin, NodeNG): # pylint: disable=redefined-builtin + """Class representing an :class:`ast.Ellipsis` node. + + An :class:`Ellipsis` is the ``...`` syntax. + + >>> node = astroid.extract_node('...') + >>> node + + """ + + def bool_value(self, context=None): + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + For an :class:`Ellipsis` this is always ``True``. + :rtype: bool + """ + return True + + +class EmptyNode(mixins.NoChildrenMixin, NodeNG): + """Holds an arbitrary object in the :attr:`LocalsDictNodeNG.locals`.""" + + object = None + + +class ExceptHandler(mixins.MultiLineBlockMixin, mixins.AssignTypeMixin, Statement): + """Class representing an :class:`ast.ExceptHandler`. node. + + An :class:`ExceptHandler` is an ``except`` block on a try-except. + + >>> node = astroid.extract_node(''' + try: + do_something() + except Exception as error: + print("Error!") + ''') + >>> node + + >>> >>> node.handlers + [] + """ + + _astroid_fields = ("type", "name", "body") + _multi_line_block_fields = ("body",) + type = None + """The types that the block handles. + + :type: Tuple or NodeNG or None + """ + name = None + """The name that the caught exception is assigned to. + + :type: AssignName or None + """ + body = None + """The contents of the block. + + :type: list(NodeNG) or None + """ + + def get_children(self): + if self.type is not None: + yield self.type + + if self.name is not None: + yield self.name + + yield from self.body + + # pylint: disable=redefined-builtin; had to use the same name as builtin ast module. + def postinit(self, type=None, name=None, body=None): + """Do some setup after initialisation. + + :param type: The types that the block handles. + :type type: Tuple or NodeNG or None + + :param name: The name that the caught exception is assigned to. + :type name: AssignName or None + + :param body:The contents of the block. + :type body: list(NodeNG) or None + """ + self.type = type + self.name = name + self.body = body + + @decorators.cachedproperty + def blockstart_tolineno(self): + """The line on which the beginning of this block ends. + + :type: int + """ + if self.name: + return self.name.tolineno + if self.type: + return self.type.tolineno + return self.lineno + + def catch(self, exceptions): # pylint: disable=redefined-outer-name + """Check if this node handles any of the given exceptions. + + If ``exceptions`` is empty, this will default to ``True``. + + :param exceptions: The name of the exceptions to check for. + :type exceptions: list(str) + """ + if self.type is None or exceptions is None: + return True + for node in self.type._get_name_nodes(): + if node.name in exceptions: + return True + return False + + +class Exec(Statement): + """Class representing the ``exec`` statement. + + >>> node = astroid.extract_node('exec "True"') + >>> node + + """ + + _astroid_fields = ("expr", "globals", "locals") + expr = None + """The expression to be executed. + + :type: NodeNG or None + """ + globals = None + """The globals dictionary to execute with. + + :type: NodeNG or None + """ + locals = None + """The locals dictionary to execute with. + + :type: NodeNG or None + """ + + # pylint: disable=redefined-builtin; had to use the same name as builtin ast module. + def postinit(self, expr=None, globals=None, locals=None): + """Do some setup after initialisation. + + :param expr: The expression to be executed. + :type expr: NodeNG or None + + :param globals:The globals dictionary to execute with. + :type globals: NodeNG or None + + :param locals: The locals dictionary to execute with. + :type locals: NodeNG or None + """ + self.expr = expr + self.globals = globals + self.locals = locals + + +class ExtSlice(NodeNG): + """Class representing an :class:`ast.ExtSlice` node. + + An :class:`ExtSlice` is a complex slice expression. + + >>> node = astroid.extract_node('l[1:3, 5]') + >>> node + + >>> node.slice + + """ + + _astroid_fields = ("dims",) + dims = None + """The simple dimensions that form the complete slice. + + :type: list(NodeNG) or None + """ + + def postinit(self, dims=None): + """Do some setup after initialisation. + + :param dims: The simple dimensions that form the complete slice. + :type dims: list(NodeNG) or None + """ + self.dims = dims + + +class For( + mixins.MultiLineBlockMixin, + mixins.BlockRangeMixIn, + mixins.AssignTypeMixin, + Statement, +): + """Class representing an :class:`ast.For` node. + + >>> node = astroid.extract_node('for thing in things: print(thing)') + >>> node + + """ + + _astroid_fields = ("target", "iter", "body", "orelse") + _other_other_fields = ("type_annotation",) + _multi_line_block_fields = ("body", "orelse") + target = None + """What the loop assigns to. + + :type: NodeNG or None + """ + iter = None + """What the loop iterates over. + + :type: NodeNG or None + """ + body = None + """The contents of the body of the loop. + + :type: list(NodeNG) or None + """ + orelse = None + """The contents of the ``else`` block of the loop. + + :type: list(NodeNG) or None + """ + type_annotation = None + """If present, this will contain the type annotation passed by a type comment + + :type: NodeNG or None + """ + + # pylint: disable=redefined-builtin; had to use the same name as builtin ast module. + def postinit( + self, target=None, iter=None, body=None, orelse=None, type_annotation=None + ): + """Do some setup after initialisation. + + :param target: What the loop assigns to. + :type target: NodeNG or None + + :param iter: What the loop iterates over. + :type iter: NodeNG or None + + :param body: The contents of the body of the loop. + :type body: list(NodeNG) or None + + :param orelse: The contents of the ``else`` block of the loop. + :type orelse: list(NodeNG) or None + """ + self.target = target + self.iter = iter + self.body = body + self.orelse = orelse + self.type_annotation = type_annotation + + optional_assign = True + """Whether this node optionally assigns a variable. + + This is always ``True`` for :class:`For` nodes. + + :type: bool + """ + + @decorators.cachedproperty + def blockstart_tolineno(self): + """The line on which the beginning of this block ends. + + :type: int + """ + return self.iter.tolineno + + def get_children(self): + yield self.target + yield self.iter + + yield from self.body + yield from self.orelse + + +class AsyncFor(For): + """Class representing an :class:`ast.AsyncFor` node. + + An :class:`AsyncFor` is an asynchronous :class:`For` built with + the ``async`` keyword. + + >>> node = astroid.extract_node(''' + async def func(things): + async for thing in things: + print(thing) + ''') + >>> node + + >>> node.body[0] + + """ + + +class Await(NodeNG): + """Class representing an :class:`ast.Await` node. + + An :class:`Await` is the ``await`` keyword. + + >>> node = astroid.extract_node(''' + async def func(things): + await other_func() + ''') + >>> node + + >>> node.body[0] + + >>> list(node.body[0].get_children())[0] + + """ + + _astroid_fields = ("value",) + value = None + """What to wait for. + + :type: NodeNG or None + """ + + def postinit(self, value=None): + """Do some setup after initialisation. + + :param value: What to wait for. + :type value: NodeNG or None + """ + self.value = value + + def get_children(self): + yield self.value + + +class ImportFrom(mixins.NoChildrenMixin, mixins.ImportFromMixin, Statement): + """Class representing an :class:`ast.ImportFrom` node. + + >>> node = astroid.extract_node('from my_package import my_module') + >>> node + + """ + + _other_fields = ("modname", "names", "level") + + def __init__( + self, fromname, names, level=0, lineno=None, col_offset=None, parent=None + ): + """ + :param fromname: The module that is being imported from. + :type fromname: str or None + + :param names: What is being imported from the module. + :type names: list(tuple(str, str or None)) + + :param level: The level of relative import. + :type level: int + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.modname = fromname + """The module that is being imported from. + + This is ``None`` for relative imports. + + :type: str or None + """ + + self.names = names + """What is being imported from the module. + + Each entry is a :class:`tuple` of the name being imported, + and the alias that the name is assigned to (if any). + + :type: list(tuple(str, str or None)) + """ + + self.level = level + """The level of relative import. + + Essentially this is the number of dots in the import. + This is always 0 for absolute imports. + + :type: int + """ + + super().__init__(lineno, col_offset, parent) + + +class Attribute(NodeNG): + """Class representing an :class:`ast.Attribute` node.""" + + _astroid_fields = ("expr",) + _other_fields = ("attrname",) + expr = None + """The name that this node represents. + + :type: Name or None + """ + + def __init__(self, attrname=None, lineno=None, col_offset=None, parent=None): + """ + :param attrname: The name of the attribute. + :type attrname: str or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.attrname = attrname + """The name of the attribute. + + :type: str or None + """ + + super().__init__(lineno, col_offset, parent) + + def postinit(self, expr=None): + """Do some setup after initialisation. + + :param expr: The name that this node represents. + :type expr: Name or None + """ + self.expr = expr + + def get_children(self): + yield self.expr + + +class Global(mixins.NoChildrenMixin, Statement): + """Class representing an :class:`ast.Global` node. + + >>> node = astroid.extract_node('global a_global') + >>> node + + """ + + _other_fields = ("names",) + + def __init__(self, names, lineno=None, col_offset=None, parent=None): + """ + :param names: The names being declared as global. + :type names: list(str) + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.names = names + """The names being declared as global. + + :type: list(str) + """ + + super().__init__(lineno, col_offset, parent) + + def _infer_name(self, frame, name): + return name + + +class If(mixins.MultiLineBlockMixin, mixins.BlockRangeMixIn, Statement): + """Class representing an :class:`ast.If` node. + + >>> node = astroid.extract_node('if condition: print(True)') + >>> node + + """ + + _astroid_fields = ("test", "body", "orelse") + _multi_line_block_fields = ("body", "orelse") + test = None + """The condition that the statement tests. + + :type: NodeNG or None + """ + body = None + """The contents of the block. + + :type: list(NodeNG) or None + """ + orelse = None + """The contents of the ``else`` block. + + :type: list(NodeNG) or None + """ + + def postinit(self, test=None, body=None, orelse=None): + """Do some setup after initialisation. + + :param test: The condition that the statement tests. + :type test: NodeNG or None + + :param body: The contents of the block. + :type body: list(NodeNG) or None + + :param orelse: The contents of the ``else`` block. + :type orelse: list(NodeNG) or None + """ + self.test = test + self.body = body + self.orelse = orelse + + @decorators.cachedproperty + def blockstart_tolineno(self): + """The line on which the beginning of this block ends. + + :type: int + """ + return self.test.tolineno + + def block_range(self, lineno): + """Get a range from the given line number to where this node ends. + + :param lineno: The line number to start the range at. + :type lineno: int + + :returns: The range of line numbers that this node belongs to, + starting at the given line number. + :rtype: tuple(int, int) + """ + if lineno == self.body[0].fromlineno: + return lineno, lineno + if lineno <= self.body[-1].tolineno: + return lineno, self.body[-1].tolineno + return self._elsed_block_range(lineno, self.orelse, self.body[0].fromlineno - 1) + + def get_children(self): + yield self.test + + yield from self.body + yield from self.orelse + + def has_elif_block(self): + return len(self.orelse) == 1 and isinstance(self.orelse[0], If) + + +class IfExp(NodeNG): + """Class representing an :class:`ast.IfExp` node. + + >>> node = astroid.extract_node('value if condition else other') + >>> node + + """ + + _astroid_fields = ("test", "body", "orelse") + test = None + """The condition that the statement tests. + + :type: NodeNG or None + """ + body = None + """The contents of the block. + + :type: list(NodeNG) or None + """ + orelse = None + """The contents of the ``else`` block. + + :type: list(NodeNG) or None + """ + + def postinit(self, test=None, body=None, orelse=None): + """Do some setup after initialisation. + + :param test: The condition that the statement tests. + :type test: NodeNG or None + + :param body: The contents of the block. + :type body: list(NodeNG) or None + + :param orelse: The contents of the ``else`` block. + :type orelse: list(NodeNG) or None + """ + self.test = test + self.body = body + self.orelse = orelse + + def get_children(self): + yield self.test + yield self.body + yield self.orelse + + def op_left_associative(self): + # `1 if True else 2 if False else 3` is parsed as + # `1 if True else (2 if False else 3)` + return False + + +class Import(mixins.NoChildrenMixin, mixins.ImportFromMixin, Statement): + """Class representing an :class:`ast.Import` node. + + >>> node = astroid.extract_node('import astroid') + >>> node + + """ + + _other_fields = ("names",) + + def __init__(self, names=None, lineno=None, col_offset=None, parent=None): + """ + :param names: The names being imported. + :type names: list(tuple(str, str or None)) or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.names = names + """The names being imported. + + Each entry is a :class:`tuple` of the name being imported, + and the alias that the name is assigned to (if any). + + :type: list(tuple(str, str or None)) or None + """ + + super().__init__(lineno, col_offset, parent) + + +class Index(NodeNG): + """Class representing an :class:`ast.Index` node. + + An :class:`Index` is a simple subscript. + + >>> node = astroid.extract_node('things[1]') + >>> node + + >>> node.slice + + """ + + _astroid_fields = ("value",) + value = None + """The value to subscript with. + + :type: NodeNG or None + """ + + def postinit(self, value=None): + """Do some setup after initialisation. + + :param value: The value to subscript with. + :type value: NodeNG or None + """ + self.value = value + + def get_children(self): + yield self.value + + +class Keyword(NodeNG): + """Class representing an :class:`ast.keyword` node. + + >>> node = astroid.extract_node('function(a_kwarg=True)') + >>> node + + >>> node.keywords + [] + """ + + _astroid_fields = ("value",) + _other_fields = ("arg",) + value = None + """The value being assigned to the keyword argument. + + :type: NodeNG or None + """ + + def __init__(self, arg=None, lineno=None, col_offset=None, parent=None): + """ + :param arg: The argument being assigned to. + :type arg: Name or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.arg = arg + """The argument being assigned to. + + :type: Name or None + """ + + super().__init__(lineno, col_offset, parent) + + def postinit(self, value=None): + """Do some setup after initialisation. + + :param value: The value being assigned to the ketword argument. + :type value: NodeNG or None + """ + self.value = value + + def get_children(self): + yield self.value + + +class List(_BaseContainer): + """Class representing an :class:`ast.List` node. + + >>> node = astroid.extract_node('[1, 2, 3]') + >>> node + + """ + + _other_fields = ("ctx",) + + def __init__(self, ctx=None, lineno=None, col_offset=None, parent=None): + """ + :param ctx: Whether the list is assigned to or loaded from. + :type ctx: Context or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.ctx = ctx + """Whether the list is assigned to or loaded from. + + :type: Context or None + """ + + super().__init__(lineno, col_offset, parent) + + def pytype(self): + """Get the name of the type that this node represents. + + :returns: The name of the type. + :rtype: str + """ + return "%s.list" % BUILTINS + + def getitem(self, index, context=None): + """Get an item from this node. + + :param index: The node to use as a subscript index. + :type index: Const or Slice + """ + return _container_getitem(self, self.elts, index, context=context) + + +class Nonlocal(mixins.NoChildrenMixin, Statement): + """Class representing an :class:`ast.Nonlocal` node. + + >>> node = astroid.extract_node(''' + def function(): + nonlocal var + ''') + >>> node + + >>> node.body[0] + + """ + + _other_fields = ("names",) + + def __init__(self, names, lineno=None, col_offset=None, parent=None): + """ + :param names: The names being declared as not local. + :type names: list(str) + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.names = names + """The names being declared as not local. + + :type: list(str) + """ + + super().__init__(lineno, col_offset, parent) + + def _infer_name(self, frame, name): + return name + + +class Pass(mixins.NoChildrenMixin, Statement): + """Class representing an :class:`ast.Pass` node. + + >>> node = astroid.extract_node('pass') + >>> node + + """ + + +class Print(Statement): + """Class representing an :class:`ast.Print` node. + + >>> node = astroid.extract_node('print "A message"') + >>> node + + """ + + _astroid_fields = ("dest", "values") + dest = None + """Where to print to. + + :type: NodeNG or None + """ + values = None + """What to print. + + :type: list(NodeNG) or None + """ + + def __init__(self, nl=None, lineno=None, col_offset=None, parent=None): + """ + :param nl: Whether to print a new line. + :type nl: bool or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.nl = nl + """Whether to print a new line. + + :type: bool or None + """ + + super().__init__(lineno, col_offset, parent) + + def postinit(self, dest=None, values=None): + """Do some setup after initialisation. + + :param dest: Where to print to. + :type dest: NodeNG or None + + :param values: What to print. + :type values: list(NodeNG) or None + """ + self.dest = dest + self.values = values + + +class Raise(Statement): + """Class representing an :class:`ast.Raise` node. + + >>> node = astroid.extract_node('raise RuntimeError("Something bad happened!")') + >>> node + + """ + + exc = None + """What is being raised. + + :type: NodeNG or None + """ + _astroid_fields = ("exc", "cause") + cause = None + """The exception being used to raise this one. + + :type: NodeNG or None + """ + + def postinit(self, exc=None, cause=None): + """Do some setup after initialisation. + + :param exc: What is being raised. + :type exc: NodeNG or None + + :param cause: The exception being used to raise this one. + :type cause: NodeNG or None + """ + self.exc = exc + self.cause = cause + + def raises_not_implemented(self): + """Check if this node raises a :class:`NotImplementedError`. + + :returns: True if this node raises a :class:`NotImplementedError`, + False otherwise. + :rtype: bool + """ + if not self.exc: + return False + for name in self.exc._get_name_nodes(): + if name.name == "NotImplementedError": + return True + return False + + def get_children(self): + if self.exc is not None: + yield self.exc + + if self.cause is not None: + yield self.cause + + +class Return(Statement): + """Class representing an :class:`ast.Return` node. + + >>> node = astroid.extract_node('return True') + >>> node + + """ + + _astroid_fields = ("value",) + value = None + """The value being returned. + + :type: NodeNG or None + """ + + def postinit(self, value=None): + """Do some setup after initialisation. + + :param value: The value being returned. + :type value: NodeNG or None + """ + self.value = value + + def get_children(self): + if self.value is not None: + yield self.value + + def is_tuple_return(self): + return isinstance(self.value, Tuple) + + def _get_return_nodes_skip_functions(self): + yield self + + +class Set(_BaseContainer): + """Class representing an :class:`ast.Set` node. + + >>> node = astroid.extract_node('{1, 2, 3}') + >>> node + + """ + + def pytype(self): + """Get the name of the type that this node represents. + + :returns: The name of the type. + :rtype: str + """ + return "%s.set" % BUILTINS + + +class Slice(NodeNG): + """Class representing an :class:`ast.Slice` node. + + >>> node = astroid.extract_node('things[1:3]') + >>> node + + >>> node.slice + + """ + + _astroid_fields = ("lower", "upper", "step") + lower = None + """The lower index in the slice. + + :type: NodeNG or None + """ + upper = None + """The upper index in the slice. + + :type: NodeNG or None + """ + step = None + """The step to take between indexes. + + :type: NodeNG or None + """ + + def postinit(self, lower=None, upper=None, step=None): + """Do some setup after initialisation. + + :param lower: The lower index in the slice. + :value lower: NodeNG or None + + :param upper: The upper index in the slice. + :value upper: NodeNG or None + + :param step: The step to take between index. + :param step: NodeNG or None + """ + self.lower = lower + self.upper = upper + self.step = step + + def _wrap_attribute(self, attr): + """Wrap the empty attributes of the Slice in a Const node.""" + if not attr: + const = const_factory(attr) + const.parent = self + return const + return attr + + @decorators.cachedproperty + def _proxied(self): + builtins = MANAGER.builtins_module + return builtins.getattr("slice")[0] + + def pytype(self): + """Get the name of the type that this node represents. + + :returns: The name of the type. + :rtype: str + """ + return "%s.slice" % BUILTINS + + def igetattr(self, attrname, context=None): + """Infer the possible values of the given attribute on the slice. + + :param attrname: The name of the attribute to infer. + :type attrname: str + + :returns: The inferred possible values. + :rtype: iterable(NodeNG) + """ + if attrname == "start": + yield self._wrap_attribute(self.lower) + elif attrname == "stop": + yield self._wrap_attribute(self.upper) + elif attrname == "step": + yield self._wrap_attribute(self.step) + else: + yield from self.getattr(attrname, context=context) + + def getattr(self, attrname, context=None): + return self._proxied.getattr(attrname, context) + + def get_children(self): + if self.lower is not None: + yield self.lower + + if self.upper is not None: + yield self.upper + + if self.step is not None: + yield self.step + + +class Starred(mixins.ParentAssignTypeMixin, NodeNG): + """Class representing an :class:`ast.Starred` node. + + >>> node = astroid.extract_node('*args') + >>> node + + """ + + _astroid_fields = ("value",) + _other_fields = ("ctx",) + value = None + """What is being unpacked. + + :type: NodeNG or None + """ + + def __init__(self, ctx=None, lineno=None, col_offset=None, parent=None): + """ + :param ctx: Whether the list is assigned to or loaded from. + :type ctx: Context or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.ctx = ctx + """Whether the starred item is assigned to or loaded from. + + :type: Context or None + """ + + super().__init__(lineno=lineno, col_offset=col_offset, parent=parent) + + def postinit(self, value=None): + """Do some setup after initialisation. + + :param value: What is being unpacked. + :type value: NodeNG or None + """ + self.value = value + + def get_children(self): + yield self.value + + +class Subscript(NodeNG): + """Class representing an :class:`ast.Subscript` node. + + >>> node = astroid.extract_node('things[1:3]') + >>> node + + """ + + _astroid_fields = ("value", "slice") + _other_fields = ("ctx",) + value = None + """What is being indexed. + + :type: NodeNG or None + """ + slice = None + """The slice being used to lookup. + + :type: NodeNG or None + """ + + def __init__(self, ctx=None, lineno=None, col_offset=None, parent=None): + """ + :param ctx: Whether the subscripted item is assigned to or loaded from. + :type ctx: Context or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.ctx = ctx + """Whether the subscripted item is assigned to or loaded from. + + :type: Context or None + """ + + super().__init__(lineno=lineno, col_offset=col_offset, parent=parent) + + # pylint: disable=redefined-builtin; had to use the same name as builtin ast module. + def postinit(self, value=None, slice=None): + """Do some setup after initialisation. + + :param value: What is being indexed. + :type value: NodeNG or None + + :param slice: The slice being used to lookup. + :type slice: NodeNG or None + """ + self.value = value + self.slice = slice + + def get_children(self): + yield self.value + yield self.slice + + +class TryExcept(mixins.MultiLineBlockMixin, mixins.BlockRangeMixIn, Statement): + """Class representing an :class:`ast.TryExcept` node. + + >>> node = astroid.extract_node(''' + try: + do_something() + except Exception as error: + print("Error!") + ''') + >>> node + + """ + + _astroid_fields = ("body", "handlers", "orelse") + _multi_line_block_fields = ("body", "handlers", "orelse") + body = None + """The contents of the block to catch exceptions from. + + :type: list(NodeNG) or None + """ + handlers = None + """The exception handlers. + + :type: list(ExceptHandler) or None + """ + orelse = None + """The contents of the ``else`` block. + + :type: list(NodeNG) or None + """ + + def postinit(self, body=None, handlers=None, orelse=None): + """Do some setup after initialisation. + + :param body: The contents of the block to catch exceptions from. + :type body: list(NodeNG) or None + + :param handlers: The exception handlers. + :type handlers: list(ExceptHandler) or None + + :param orelse: The contents of the ``else`` block. + :type orelse: list(NodeNG) or None + """ + self.body = body + self.handlers = handlers + self.orelse = orelse + + def _infer_name(self, frame, name): + return name + + def block_range(self, lineno): + """Get a range from the given line number to where this node ends. + + :param lineno: The line number to start the range at. + :type lineno: int + + :returns: The range of line numbers that this node belongs to, + starting at the given line number. + :rtype: tuple(int, int) + """ + last = None + for exhandler in self.handlers: + if exhandler.type and lineno == exhandler.type.fromlineno: + return lineno, lineno + if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno: + return lineno, exhandler.body[-1].tolineno + if last is None: + last = exhandler.body[0].fromlineno - 1 + return self._elsed_block_range(lineno, self.orelse, last) + + def get_children(self): + yield from self.body + + yield from self.handlers or () + yield from self.orelse or () + + +class TryFinally(mixins.MultiLineBlockMixin, mixins.BlockRangeMixIn, Statement): + """Class representing an :class:`ast.TryFinally` node. + + >>> node = astroid.extract_node(''' + try: + do_something() + except Exception as error: + print("Error!") + finally: + print("Cleanup!") + ''') + >>> node + + """ + + _astroid_fields = ("body", "finalbody") + _multi_line_block_fields = ("body", "finalbody") + body = None + """The try-except that the finally is attached to. + + :type: list(TryExcept) or None + """ + finalbody = None + """The contents of the ``finally`` block. + + :type: list(NodeNG) or None + """ + + def postinit(self, body=None, finalbody=None): + """Do some setup after initialisation. + + :param body: The try-except that the finally is attached to. + :type body: list(TryExcept) or None + + :param finalbody: The contents of the ``finally`` block. + :type finalbody: list(NodeNG) or None + """ + self.body = body + self.finalbody = finalbody + + def block_range(self, lineno): + """Get a range from the given line number to where this node ends. + + :param lineno: The line number to start the range at. + :type lineno: int + + :returns: The range of line numbers that this node belongs to, + starting at the given line number. + :rtype: tuple(int, int) + """ + child = self.body[0] + # py2.5 try: except: finally: + if ( + isinstance(child, TryExcept) + and child.fromlineno == self.fromlineno + and child.tolineno >= lineno > self.fromlineno + ): + return child.block_range(lineno) + return self._elsed_block_range(lineno, self.finalbody) + + def get_children(self): + yield from self.body + yield from self.finalbody + + +class Tuple(_BaseContainer): + """Class representing an :class:`ast.Tuple` node. + + >>> node = astroid.extract_node('(1, 2, 3)') + >>> node + + """ + + _other_fields = ("ctx",) + + def __init__(self, ctx=None, lineno=None, col_offset=None, parent=None): + """ + :param ctx: Whether the tuple is assigned to or loaded from. + :type ctx: Context or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.ctx = ctx + """Whether the tuple is assigned to or loaded from. + + :type: Context or None + """ + + super().__init__(lineno, col_offset, parent) + + def pytype(self): + """Get the name of the type that this node represents. + + :returns: The name of the type. + :rtype: str + """ + return "%s.tuple" % BUILTINS + + def getitem(self, index, context=None): + """Get an item from this node. + + :param index: The node to use as a subscript index. + :type index: Const or Slice + """ + return _container_getitem(self, self.elts, index, context=context) + + +class UnaryOp(NodeNG): + """Class representing an :class:`ast.UnaryOp` node. + + >>> node = astroid.extract_node('-5') + >>> node + + """ + + _astroid_fields = ("operand",) + _other_fields = ("op",) + operand = None + """What the unary operator is applied to. + + :type: NodeNG or None + """ + + def __init__(self, op=None, lineno=None, col_offset=None, parent=None): + """ + :param op: The operator. + :type: str or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.op = op + """The operator. + + :type: str or None + """ + + super().__init__(lineno, col_offset, parent) + + def postinit(self, operand=None): + """Do some setup after initialisation. + + :param operand: What the unary operator is applied to. + :type operand: NodeNG or None + """ + self.operand = operand + + # This is set by inference.py + def _infer_unaryop(self, context=None): + raise NotImplementedError + + def type_errors(self, context=None): + """Get a list of type errors which can occur during inference. + + Each TypeError is represented by a :class:`BadBinaryOperationMessage`, + which holds the original exception. + + :returns: The list of possible type errors. + :rtype: list(BadBinaryOperationMessage) + """ + try: + results = self._infer_unaryop(context=context) + return [ + result + for result in results + if isinstance(result, util.BadUnaryOperationMessage) + ] + except exceptions.InferenceError: + return [] + + def get_children(self): + yield self.operand + + def op_precedence(self): + if self.op == "not": + return OP_PRECEDENCE[self.op] + + return super().op_precedence() + + +class While(mixins.MultiLineBlockMixin, mixins.BlockRangeMixIn, Statement): + """Class representing an :class:`ast.While` node. + + >>> node = astroid.extract_node(''' + while condition(): + print("True") + ''') + >>> node + + """ + + _astroid_fields = ("test", "body", "orelse") + _multi_line_block_fields = ("body", "orelse") + test = None + """The condition that the loop tests. + + :type: NodeNG or None + """ + body = None + """The contents of the loop. + + :type: list(NodeNG) or None + """ + orelse = None + """The contents of the ``else`` block. + + :type: list(NodeNG) or None + """ + + def postinit(self, test=None, body=None, orelse=None): + """Do some setup after initialisation. + + :param test: The condition that the loop tests. + :type test: NodeNG or None + + :param body: The contents of the loop. + :type body: list(NodeNG) or None + + :param orelse: The contents of the ``else`` block. + :type orelse: list(NodeNG) or None + """ + self.test = test + self.body = body + self.orelse = orelse + + @decorators.cachedproperty + def blockstart_tolineno(self): + """The line on which the beginning of this block ends. + + :type: int + """ + return self.test.tolineno + + def block_range(self, lineno): + """Get a range from the given line number to where this node ends. + + :param lineno: The line number to start the range at. + :type lineno: int + + :returns: The range of line numbers that this node belongs to, + starting at the given line number. + :rtype: tuple(int, int) + """ + return self._elsed_block_range(lineno, self.orelse) + + def get_children(self): + yield self.test + + yield from self.body + yield from self.orelse + + def _get_yield_nodes_skip_lambdas(self): + """A While node can contain a Yield node in the test""" + yield from self.test._get_yield_nodes_skip_lambdas() + yield from super()._get_yield_nodes_skip_lambdas() + + +class With( + mixins.MultiLineBlockMixin, + mixins.BlockRangeMixIn, + mixins.AssignTypeMixin, + Statement, +): + """Class representing an :class:`ast.With` node. + + >>> node = astroid.extract_node(''' + with open(file_path) as file_: + print(file_.read()) + ''') + >>> node + + """ + + _astroid_fields = ("items", "body") + _other_other_fields = ("type_annotation",) + _multi_line_block_fields = ("body",) + items = None + """The pairs of context managers and the names they are assigned to. + + :type: list(tuple(NodeNG, AssignName or None)) or None + """ + body = None + """The contents of the ``with`` block. + + :type: list(NodeNG) or None + """ + type_annotation = None + """If present, this will contain the type annotation passed by a type comment + + :type: NodeNG or None + """ + + def postinit(self, items=None, body=None, type_annotation=None): + """Do some setup after initialisation. + + :param items: The pairs of context managers and the names + they are assigned to. + :type items: list(tuple(NodeNG, AssignName or None)) or None + + :param body: The contents of the ``with`` block. + :type body: list(NodeNG) or None + """ + self.items = items + self.body = body + self.type_annotation = type_annotation + + @decorators.cachedproperty + def blockstart_tolineno(self): + """The line on which the beginning of this block ends. + + :type: int + """ + return self.items[-1][0].tolineno + + def get_children(self): + """Get the child nodes below this node. + + :returns: The children. + :rtype: iterable(NodeNG) + """ + for expr, var in self.items: + yield expr + if var: + yield var + yield from self.body + + +class AsyncWith(With): + """Asynchronous ``with`` built with the ``async`` keyword.""" + + +class Yield(NodeNG): + """Class representing an :class:`ast.Yield` node. + + >>> node = astroid.extract_node('yield True') + >>> node + + """ + + _astroid_fields = ("value",) + value = None + """The value to yield. + + :type: NodeNG or None + """ + + def postinit(self, value=None): + """Do some setup after initialisation. + + :param value: The value to yield. + :type value: NodeNG or None + """ + self.value = value + + def get_children(self): + if self.value is not None: + yield self.value + + def _get_yield_nodes_skip_lambdas(self): + yield self + + +class YieldFrom(Yield): + """Class representing an :class:`ast.YieldFrom` node.""" + + +class DictUnpack(mixins.NoChildrenMixin, NodeNG): + """Represents the unpacking of dicts into dicts using :pep:`448`.""" + + +class FormattedValue(NodeNG): + """Class representing an :class:`ast.FormattedValue` node. + + Represents a :pep:`498` format string. + + >>> node = astroid.extract_node('f"Format {type_}"') + >>> node + + >>> node.values + [, ] + """ + + _astroid_fields = ("value", "format_spec") + value = None + """The value to be formatted into the string. + + :type: NodeNG or None + """ + conversion = None + """The type of formatting to be applied to the value. + + .. seealso:: + :class:`ast.FormattedValue` + + :type: int or None + """ + format_spec = None + """The formatting to be applied to the value. + + .. seealso:: + :class:`ast.FormattedValue` + + :type: JoinedStr or None + """ + + def postinit(self, value, conversion=None, format_spec=None): + """Do some setup after initialisation. + + :param value: The value to be formatted into the string. + :type value: NodeNG + + :param conversion: The type of formatting to be applied to the value. + :type conversion: int or None + + :param format_spec: The formatting to be applied to the value. + :type format_spec: JoinedStr or None + """ + self.value = value + self.conversion = conversion + self.format_spec = format_spec + + def get_children(self): + yield self.value + + if self.format_spec is not None: + yield self.format_spec + + +class JoinedStr(NodeNG): + """Represents a list of string expressions to be joined. + + >>> node = astroid.extract_node('f"Format {type_}"') + >>> node + + """ + + _astroid_fields = ("values",) + values = None + """The string expressions to be joined. + + :type: list(FormattedValue or Const) or None + """ + + def postinit(self, values=None): + """Do some setup after initialisation. + + :param value: The string expressions to be joined. + + :type: list(FormattedValue or Const) or None + """ + self.values = values + + def get_children(self): + yield from self.values + + +class NamedExpr(mixins.AssignTypeMixin, NodeNG): + """Represents the assignment from the assignment expression + + >>> module = astroid.parse('if a := 1: pass') + >>> module.body[0].test + + """ + + _astroid_fields = ("target", "value") + target = None + """The assignment target + + :type: Name + """ + value = None + """The value that gets assigned in the expression + + :type: NodeNG + """ + + def postinit(self, target, value): + self.target = target + self.value = value + + +class Unknown(mixins.AssignTypeMixin, NodeNG): + """This node represents a node in a constructed AST where + introspection is not possible. At the moment, it's only used in + the args attribute of FunctionDef nodes where function signature + introspection failed. + """ + + name = "Unknown" + + def qname(self): + return "Unknown" + + def infer(self, context=None, **kwargs): + """Inference on an Unknown node immediately terminates.""" + yield util.Uninferable + + +class EvaluatedObject(NodeNG): + """Contains an object that has already been inferred + + This class is useful to pre-evaluate a particular node, + with the resulting class acting as the non-evaluated node. + """ + + name = "EvaluatedObject" + _astroid_fields = ("original",) + _other_fields = ("value",) + + original = None + """The original node that has already been evaluated + + :type: NodeNG + """ + + value = None + """The inferred value + + :type: Union[Uninferable, NodeNG] + """ + + def __init__(self, original, value): + self.original = original + self.value = value + super().__init__( + lineno=self.original.lineno, + col_offset=self.original.col_offset, + parent=self.original.parent, + ) + + def infer(self, context=None, **kwargs): + yield self.value + + +# constants ############################################################## + +CONST_CLS = { + list: List, + tuple: Tuple, + dict: Dict, + set: Set, + type(None): Const, + type(NotImplemented): Const, +} +if PY38: + CONST_CLS[type(...)] = Const + + +def _update_const_classes(): + """update constant classes, so the keys of CONST_CLS can be reused""" + klasses = (bool, int, float, complex, str, bytes) + for kls in klasses: + CONST_CLS[kls] = Const + + +_update_const_classes() + + +def _two_step_initialization(cls, value): + instance = cls() + instance.postinit(value) + return instance + + +def _dict_initialization(cls, value): + if isinstance(value, dict): + value = tuple(value.items()) + return _two_step_initialization(cls, value) + + +_CONST_CLS_CONSTRUCTORS = { + List: _two_step_initialization, + Tuple: _two_step_initialization, + Dict: _dict_initialization, + Set: _two_step_initialization, + Const: lambda cls, value: cls(value), +} + + +def const_factory(value): + """return an astroid node for a python value""" + # XXX we should probably be stricter here and only consider stuff in + # CONST_CLS or do better treatment: in case where value is not in CONST_CLS, + # we should rather recall the builder on this value than returning an empty + # node (another option being that const_factory shouldn't be called with something + # not in CONST_CLS) + assert not isinstance(value, NodeNG) + + # Hack for ignoring elements of a sequence + # or a mapping, in order to avoid transforming + # each element to an AST. This is fixed in 2.0 + # and this approach is a temporary hack. + if isinstance(value, (list, set, tuple, dict)): + elts = [] + else: + elts = value + + try: + initializer_cls = CONST_CLS[value.__class__] + initializer = _CONST_CLS_CONSTRUCTORS[initializer_cls] + return initializer(initializer_cls, elts) + except (KeyError, AttributeError): + node = EmptyNode() + node.object = value + return node + + +def is_from_decorator(node): + """Return True if the given node is the child of a decorator""" + parent = node.parent + while parent is not None: + if isinstance(parent, Decorators): + return True + parent = parent.parent + return False diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/nodes.py b/Display/.venv/lib/python3.7/site-packages/astroid/nodes.py new file mode 100644 index 0000000..4ce4ebe --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/nodes.py @@ -0,0 +1,176 @@ +# Copyright (c) 2006-2011, 2013 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2010 Daniel Harding +# Copyright (c) 2014-2020 Claudiu Popa +# Copyright (c) 2014 Google, Inc. +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2016 Jared Garst +# Copyright (c) 2017 Ashley Whetter +# Copyright (c) 2017 rr- +# Copyright (c) 2018 Bryce Guinta + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""Every available node class. + +.. seealso:: + :doc:`ast documentation ` + +All nodes inherit from :class:`~astroid.node_classes.NodeNG`. +""" +# pylint: disable=unused-import,redefined-builtin + +from astroid.node_classes import ( + Arguments, + AssignAttr, + Assert, + Assign, + AnnAssign, + AssignName, + AugAssign, + Repr, + BinOp, + BoolOp, + Break, + Call, + Compare, + Comprehension, + Const, + Continue, + Decorators, + DelAttr, + DelName, + Delete, + Dict, + Expr, + Ellipsis, + EmptyNode, + ExceptHandler, + Exec, + ExtSlice, + For, + ImportFrom, + Attribute, + Global, + If, + IfExp, + Import, + Index, + Keyword, + List, + Name, + NamedExpr, + Nonlocal, + Pass, + Print, + Raise, + Return, + Set, + Slice, + Starred, + Subscript, + TryExcept, + TryFinally, + Tuple, + UnaryOp, + While, + With, + Yield, + YieldFrom, + const_factory, + AsyncFor, + Await, + AsyncWith, + FormattedValue, + JoinedStr, + # Node not present in the builtin ast module. + DictUnpack, + Unknown, + EvaluatedObject, +) +from astroid.scoped_nodes import ( + Module, + GeneratorExp, + Lambda, + DictComp, + ListComp, + SetComp, + FunctionDef, + ClassDef, + AsyncFunctionDef, +) + + +ALL_NODE_CLASSES = ( + AsyncFunctionDef, + AsyncFor, + AsyncWith, + Await, + Arguments, + AssignAttr, + Assert, + Assign, + AnnAssign, + AssignName, + AugAssign, + Repr, + BinOp, + BoolOp, + Break, + Call, + ClassDef, + Compare, + Comprehension, + Const, + Continue, + Decorators, + DelAttr, + DelName, + Delete, + Dict, + DictComp, + DictUnpack, + Expr, + Ellipsis, + EmptyNode, + ExceptHandler, + Exec, + ExtSlice, + For, + ImportFrom, + FunctionDef, + Attribute, + GeneratorExp, + Global, + If, + IfExp, + Import, + Index, + Keyword, + Lambda, + List, + ListComp, + Name, + NamedExpr, + Nonlocal, + Module, + Pass, + Print, + Raise, + Return, + Set, + SetComp, + Slice, + Starred, + Subscript, + TryExcept, + TryFinally, + Tuple, + UnaryOp, + While, + With, + Yield, + YieldFrom, + FormattedValue, + JoinedStr, +) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/objects.py b/Display/.venv/lib/python3.7/site-packages/astroid/objects.py new file mode 100644 index 0000000..fb782e6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/objects.py @@ -0,0 +1,314 @@ +# Copyright (c) 2015-2016, 2018-2020 Claudiu Popa +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2015 Florian Bruhin +# Copyright (c) 2016 Derek Gustafson +# Copyright (c) 2018 hippo91 +# Copyright (c) 2018 Bryce Guinta + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + + +""" +Inference objects are a way to represent composite AST nodes, +which are used only as inference results, so they can't be found in the +original AST tree. For instance, inferring the following frozenset use, +leads to an inferred FrozenSet: + + Call(func=Name('frozenset'), args=Tuple(...)) +""" + +import builtins + +from astroid import bases +from astroid import decorators +from astroid import exceptions +from astroid import MANAGER +from astroid import node_classes +from astroid import scoped_nodes +from astroid import util + + +BUILTINS = builtins.__name__ +objectmodel = util.lazy_import("interpreter.objectmodel") + + +class FrozenSet(node_classes._BaseContainer): + """class representing a FrozenSet composite node""" + + def pytype(self): + return "%s.frozenset" % BUILTINS + + def _infer(self, context=None): + yield self + + @decorators.cachedproperty + def _proxied(self): # pylint: disable=method-hidden + ast_builtins = MANAGER.builtins_module + return ast_builtins.getattr("frozenset")[0] + + +class Super(node_classes.NodeNG): + """Proxy class over a super call. + + This class offers almost the same behaviour as Python's super, + which is MRO lookups for retrieving attributes from the parents. + + The *mro_pointer* is the place in the MRO from where we should + start looking, not counting it. *mro_type* is the object which + provides the MRO, it can be both a type or an instance. + *self_class* is the class where the super call is, while + *scope* is the function where the super call is. + """ + + # pylint: disable=unnecessary-lambda + special_attributes = util.lazy_descriptor(lambda: objectmodel.SuperModel()) + + # pylint: disable=super-init-not-called + def __init__(self, mro_pointer, mro_type, self_class, scope): + self.type = mro_type + self.mro_pointer = mro_pointer + self._class_based = False + self._self_class = self_class + self._scope = scope + + def _infer(self, context=None): + yield self + + def super_mro(self): + """Get the MRO which will be used to lookup attributes in this super.""" + if not isinstance(self.mro_pointer, scoped_nodes.ClassDef): + raise exceptions.SuperError( + "The first argument to super must be a subtype of " + "type, not {mro_pointer}.", + super_=self, + ) + + if isinstance(self.type, scoped_nodes.ClassDef): + # `super(type, type)`, most likely in a class method. + self._class_based = True + mro_type = self.type + else: + mro_type = getattr(self.type, "_proxied", None) + if not isinstance(mro_type, (bases.Instance, scoped_nodes.ClassDef)): + raise exceptions.SuperError( + "The second argument to super must be an " + "instance or subtype of type, not {type}.", + super_=self, + ) + + if not mro_type.newstyle: + raise exceptions.SuperError( + "Unable to call super on old-style classes.", super_=self + ) + + mro = mro_type.mro() + if self.mro_pointer not in mro: + raise exceptions.SuperError( + "The second argument to super must be an " + "instance or subtype of type, not {type}.", + super_=self, + ) + + index = mro.index(self.mro_pointer) + return mro[index + 1 :] + + @decorators.cachedproperty + def _proxied(self): + ast_builtins = MANAGER.builtins_module + return ast_builtins.getattr("super")[0] + + def pytype(self): + return "%s.super" % BUILTINS + + def display_type(self): + return "Super of" + + @property + def name(self): + """Get the name of the MRO pointer.""" + return self.mro_pointer.name + + def qname(self): + return "super" + + def igetattr(self, name, context=None): + """Retrieve the inferred values of the given attribute name.""" + + if name in self.special_attributes: + yield self.special_attributes.lookup(name) + return + + try: + mro = self.super_mro() + # Don't let invalid MROs or invalid super calls + # leak out as is from this function. + except exceptions.SuperError as exc: + raise exceptions.AttributeInferenceError( + ( + "Lookup for {name} on {target!r} because super call {super!r} " + "is invalid." + ), + target=self, + attribute=name, + context=context, + super_=exc.super_, + ) from exc + except exceptions.MroError as exc: + raise exceptions.AttributeInferenceError( + ( + "Lookup for {name} on {target!r} failed because {cls!r} has an " + "invalid MRO." + ), + target=self, + attribute=name, + context=context, + mros=exc.mros, + cls=exc.cls, + ) from exc + found = False + for cls in mro: + if name not in cls.locals: + continue + + found = True + for inferred in bases._infer_stmts([cls[name]], context, frame=self): + if not isinstance(inferred, scoped_nodes.FunctionDef): + yield inferred + continue + + # We can obtain different descriptors from a super depending + # on what we are accessing and where the super call is. + if inferred.type == "classmethod": + yield bases.BoundMethod(inferred, cls) + elif self._scope.type == "classmethod" and inferred.type == "method": + yield inferred + elif self._class_based or inferred.type == "staticmethod": + yield inferred + elif isinstance(inferred, Property): + function = inferred.function + try: + yield from function.infer_call_result( + caller=self, context=context + ) + except exceptions.InferenceError: + yield util.Uninferable + elif bases._is_property(inferred): + # TODO: support other descriptors as well. + try: + yield from inferred.infer_call_result(self, context) + except exceptions.InferenceError: + yield util.Uninferable + else: + yield bases.BoundMethod(inferred, cls) + + if not found: + raise exceptions.AttributeInferenceError( + target=self, attribute=name, context=context + ) + + def getattr(self, name, context=None): + return list(self.igetattr(name, context=context)) + + +class ExceptionInstance(bases.Instance): + """Class for instances of exceptions + + It has special treatment for some of the exceptions's attributes, + which are transformed at runtime into certain concrete objects, such as + the case of .args. + """ + + @decorators.cachedproperty + def special_attributes(self): + qname = self.qname() + instance = objectmodel.BUILTIN_EXCEPTIONS.get( + qname, objectmodel.ExceptionInstanceModel + ) + return instance()(self) + + +class DictInstance(bases.Instance): + """Special kind of instances for dictionaries + + This instance knows the underlying object model of the dictionaries, which means + that methods such as .values or .items can be properly inferred. + """ + + # pylint: disable=unnecessary-lambda + special_attributes = util.lazy_descriptor(lambda: objectmodel.DictModel()) + + +# Custom objects tailored for dictionaries, which are used to +# disambiguate between the types of Python 2 dict's method returns +# and Python 3 (where they return set like objects). +class DictItems(bases.Proxy): + __str__ = node_classes.NodeNG.__str__ + __repr__ = node_classes.NodeNG.__repr__ + + +class DictKeys(bases.Proxy): + __str__ = node_classes.NodeNG.__str__ + __repr__ = node_classes.NodeNG.__repr__ + + +class DictValues(bases.Proxy): + __str__ = node_classes.NodeNG.__str__ + __repr__ = node_classes.NodeNG.__repr__ + + +class PartialFunction(scoped_nodes.FunctionDef): + """A class representing partial function obtained via functools.partial""" + + def __init__( + self, call, name=None, doc=None, lineno=None, col_offset=None, parent=None + ): + super().__init__(name, doc, lineno, col_offset, parent) + self.filled_positionals = len(call.positional_arguments[1:]) + self.filled_args = call.positional_arguments[1:] + self.filled_keywords = call.keyword_arguments + + def infer_call_result(self, caller=None, context=None): + if context: + current_passed_keywords = { + keyword for (keyword, _) in context.callcontext.keywords + } + for keyword, value in self.filled_keywords.items(): + if keyword not in current_passed_keywords: + context.callcontext.keywords.append((keyword, value)) + + call_context_args = context.callcontext.args or [] + context.callcontext.args = self.filled_args + call_context_args + + return super().infer_call_result(caller=caller, context=context) + + def qname(self): + return self.__class__.__name__ + + +# TODO: Hack to solve the circular import problem between node_classes and objects +# This is not needed in 2.0, which has a cleaner design overall +node_classes.Dict.__bases__ = (node_classes.NodeNG, DictInstance) + + +class Property(scoped_nodes.FunctionDef): + """Class representing a Python property""" + + def __init__( + self, function, name=None, doc=None, lineno=None, col_offset=None, parent=None + ): + self.function = function + super().__init__(name, doc, lineno, col_offset, parent) + + # pylint: disable=unnecessary-lambda + special_attributes = util.lazy_descriptor(lambda: objectmodel.PropertyModel()) + type = "property" + + def pytype(self): + return "%s.property" % BUILTINS + + def infer_call_result(self, caller=None, context=None): + raise exceptions.InferenceError("Properties are not callable") + + def infer(self, context=None, **kwargs): + return iter((self,)) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/protocols.py b/Display/.venv/lib/python3.7/site-packages/astroid/protocols.py new file mode 100644 index 0000000..2cdf554 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/protocols.py @@ -0,0 +1,780 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2014-2020 Claudiu Popa +# Copyright (c) 2014 Google, Inc. +# Copyright (c) 2014 Eevee (Alex Munroe) +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2015 Dmitry Pribysh +# Copyright (c) 2016 Derek Gustafson +# Copyright (c) 2017-2018 Ashley Whetter +# Copyright (c) 2017 Łukasz Rogalski +# Copyright (c) 2017 rr- +# Copyright (c) 2018 Nick Drozd +# Copyright (c) 2018 Ville Skyttä +# Copyright (c) 2018 Bryce Guinta +# Copyright (c) 2018 HoverHell +# Copyright (c) 2019 Hugo van Kemenade + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""this module contains a set of functions to handle python protocols for nodes +where it makes sense. +""" + +import collections +import operator as operator_mod + +import itertools + +from astroid import Store +from astroid import arguments +from astroid import bases +from astroid import context as contextmod +from astroid import exceptions +from astroid import decorators +from astroid import node_classes +from astroid import helpers +from astroid import nodes +from astroid import util + +raw_building = util.lazy_import("raw_building") +objects = util.lazy_import("objects") + + +def _reflected_name(name): + return "__r" + name[2:] + + +def _augmented_name(name): + return "__i" + name[2:] + + +_CONTEXTLIB_MGR = "contextlib.contextmanager" +BIN_OP_METHOD = { + "+": "__add__", + "-": "__sub__", + "/": "__truediv__", + "//": "__floordiv__", + "*": "__mul__", + "**": "__pow__", + "%": "__mod__", + "&": "__and__", + "|": "__or__", + "^": "__xor__", + "<<": "__lshift__", + ">>": "__rshift__", + "@": "__matmul__", +} + +REFLECTED_BIN_OP_METHOD = { + key: _reflected_name(value) for (key, value) in BIN_OP_METHOD.items() +} +AUGMENTED_OP_METHOD = { + key + "=": _augmented_name(value) for (key, value) in BIN_OP_METHOD.items() +} + +UNARY_OP_METHOD = { + "+": "__pos__", + "-": "__neg__", + "~": "__invert__", + "not": None, # XXX not '__nonzero__' +} +_UNARY_OPERATORS = { + "+": operator_mod.pos, + "-": operator_mod.neg, + "~": operator_mod.invert, + "not": operator_mod.not_, +} + + +def _infer_unary_op(obj, op): + func = _UNARY_OPERATORS[op] + value = func(obj) + return nodes.const_factory(value) + + +nodes.Tuple.infer_unary_op = lambda self, op: _infer_unary_op(tuple(self.elts), op) +nodes.List.infer_unary_op = lambda self, op: _infer_unary_op(self.elts, op) +nodes.Set.infer_unary_op = lambda self, op: _infer_unary_op(set(self.elts), op) +nodes.Const.infer_unary_op = lambda self, op: _infer_unary_op(self.value, op) +nodes.Dict.infer_unary_op = lambda self, op: _infer_unary_op(dict(self.items), op) + +# Binary operations + +BIN_OP_IMPL = { + "+": lambda a, b: a + b, + "-": lambda a, b: a - b, + "/": lambda a, b: a / b, + "//": lambda a, b: a // b, + "*": lambda a, b: a * b, + "**": lambda a, b: a ** b, + "%": lambda a, b: a % b, + "&": lambda a, b: a & b, + "|": lambda a, b: a | b, + "^": lambda a, b: a ^ b, + "<<": lambda a, b: a << b, + ">>": lambda a, b: a >> b, + "@": operator_mod.matmul, +} +for _KEY, _IMPL in list(BIN_OP_IMPL.items()): + BIN_OP_IMPL[_KEY + "="] = _IMPL + + +@decorators.yes_if_nothing_inferred +def const_infer_binary_op(self, opnode, operator, other, context, _): + not_implemented = nodes.Const(NotImplemented) + if isinstance(other, nodes.Const): + try: + impl = BIN_OP_IMPL[operator] + try: + yield nodes.const_factory(impl(self.value, other.value)) + except TypeError: + # ArithmeticError is not enough: float >> float is a TypeError + yield not_implemented + except Exception: # pylint: disable=broad-except + yield util.Uninferable + except TypeError: + yield not_implemented + elif isinstance(self.value, str) and operator == "%": + # TODO(cpopa): implement string interpolation later on. + yield util.Uninferable + else: + yield not_implemented + + +nodes.Const.infer_binary_op = const_infer_binary_op + + +def _multiply_seq_by_int(self, opnode, other, context): + node = self.__class__(parent=opnode) + filtered_elts = ( + helpers.safe_infer(elt, context) or util.Uninferable + for elt in self.elts + if elt is not util.Uninferable + ) + node.elts = list(filtered_elts) * other.value + return node + + +def _filter_uninferable_nodes(elts, context): + for elt in elts: + if elt is util.Uninferable: + yield nodes.Unknown() + else: + for inferred in elt.infer(context): + if inferred is not util.Uninferable: + yield inferred + else: + yield nodes.Unknown() + + +@decorators.yes_if_nothing_inferred +def tl_infer_binary_op(self, opnode, operator, other, context, method): + not_implemented = nodes.Const(NotImplemented) + if isinstance(other, self.__class__) and operator == "+": + node = self.__class__(parent=opnode) + node.elts = list( + itertools.chain( + _filter_uninferable_nodes(self.elts, context), + _filter_uninferable_nodes(other.elts, context), + ) + ) + yield node + elif isinstance(other, nodes.Const) and operator == "*": + if not isinstance(other.value, int): + yield not_implemented + return + yield _multiply_seq_by_int(self, opnode, other, context) + elif isinstance(other, bases.Instance) and operator == "*": + # Verify if the instance supports __index__. + as_index = helpers.class_instance_as_index(other) + if not as_index: + yield util.Uninferable + else: + yield _multiply_seq_by_int(self, opnode, as_index, context) + else: + yield not_implemented + + +nodes.Tuple.infer_binary_op = tl_infer_binary_op +nodes.List.infer_binary_op = tl_infer_binary_op + + +@decorators.yes_if_nothing_inferred +def instance_class_infer_binary_op(self, opnode, operator, other, context, method): + return method.infer_call_result(self, context) + + +bases.Instance.infer_binary_op = instance_class_infer_binary_op +nodes.ClassDef.infer_binary_op = instance_class_infer_binary_op + + +# assignment ################################################################## + +"""the assigned_stmts method is responsible to return the assigned statement +(e.g. not inferred) according to the assignment type. + +The `assign_path` argument is used to record the lhs path of the original node. +For instance if we want assigned statements for 'c' in 'a, (b,c)', assign_path +will be [1, 1] once arrived to the Assign node. + +The `context` argument is the current inference context which should be given +to any intermediary inference necessary. +""" + + +def _resolve_looppart(parts, assign_path, context): + """recursive function to resolve multiple assignments on loops""" + assign_path = assign_path[:] + index = assign_path.pop(0) + for part in parts: + if part is util.Uninferable: + continue + if not hasattr(part, "itered"): + continue + try: + itered = part.itered() + except TypeError: + continue + for stmt in itered: + index_node = nodes.Const(index) + try: + assigned = stmt.getitem(index_node, context) + except ( + AttributeError, + exceptions.AstroidTypeError, + exceptions.AstroidIndexError, + ): + continue + if not assign_path: + # we achieved to resolved the assignment path, + # don't infer the last part + yield assigned + elif assigned is util.Uninferable: + break + else: + # we are not yet on the last part of the path + # search on each possibly inferred value + try: + yield from _resolve_looppart( + assigned.infer(context), assign_path, context + ) + except exceptions.InferenceError: + break + + +@decorators.raise_if_nothing_inferred +def for_assigned_stmts(self, node=None, context=None, assign_path=None): + if isinstance(self, nodes.AsyncFor) or getattr(self, "is_async", False): + # Skip inferring of async code for now + return dict(node=self, unknown=node, assign_path=assign_path, context=context) + if assign_path is None: + for lst in self.iter.infer(context): + if isinstance(lst, (nodes.Tuple, nodes.List)): + yield from lst.elts + else: + yield from _resolve_looppart(self.iter.infer(context), assign_path, context) + return dict(node=self, unknown=node, assign_path=assign_path, context=context) + + +nodes.For.assigned_stmts = for_assigned_stmts +nodes.Comprehension.assigned_stmts = for_assigned_stmts + + +def sequence_assigned_stmts(self, node=None, context=None, assign_path=None): + if assign_path is None: + assign_path = [] + try: + index = self.elts.index(node) + except ValueError as exc: + raise exceptions.InferenceError( + "Tried to retrieve a node {node!r} which does not exist", + node=self, + assign_path=assign_path, + context=context, + ) from exc + + assign_path.insert(0, index) + return self.parent.assigned_stmts( + node=self, context=context, assign_path=assign_path + ) + + +nodes.Tuple.assigned_stmts = sequence_assigned_stmts +nodes.List.assigned_stmts = sequence_assigned_stmts + + +def assend_assigned_stmts(self, node=None, context=None, assign_path=None): + return self.parent.assigned_stmts(node=self, context=context) + + +nodes.AssignName.assigned_stmts = assend_assigned_stmts +nodes.AssignAttr.assigned_stmts = assend_assigned_stmts + + +def _arguments_infer_argname(self, name, context): + # arguments information may be missing, in which case we can't do anything + # more + if not (self.arguments or self.vararg or self.kwarg): + yield util.Uninferable + return + + functype = self.parent.type + # first argument of instance/class method + if ( + self.arguments + and getattr(self.arguments[0], "name", None) == name + and functype != "staticmethod" + ): + cls = self.parent.parent.scope() + is_metaclass = isinstance(cls, nodes.ClassDef) and cls.type == "metaclass" + # If this is a metaclass, then the first argument will always + # be the class, not an instance. + if context.boundnode and isinstance(context.boundnode, bases.Instance): + cls = context.boundnode._proxied + if is_metaclass or functype == "classmethod": + yield cls + return + if functype == "method": + yield cls.instantiate_class() + return + + if context and context.callcontext: + call_site = arguments.CallSite(context.callcontext, context.extra_context) + yield from call_site.infer_argument(self.parent, name, context) + return + + if name == self.vararg: + vararg = nodes.const_factory(()) + vararg.parent = self + if not self.arguments and self.parent.name == "__init__": + cls = self.parent.parent.scope() + vararg.elts = [cls.instantiate_class()] + yield vararg + return + if name == self.kwarg: + kwarg = nodes.const_factory({}) + kwarg.parent = self + yield kwarg + return + # if there is a default value, yield it. And then yield Uninferable to reflect + # we can't guess given argument value + try: + context = contextmod.copy_context(context) + yield from self.default_value(name).infer(context) + yield util.Uninferable + except exceptions.NoDefault: + yield util.Uninferable + + +def arguments_assigned_stmts(self, node=None, context=None, assign_path=None): + if context.callcontext: + # reset call context/name + callcontext = context.callcontext + context = contextmod.copy_context(context) + context.callcontext = None + args = arguments.CallSite(callcontext, context=context) + return args.infer_argument(self.parent, node.name, context) + return _arguments_infer_argname(self, node.name, context) + + +nodes.Arguments.assigned_stmts = arguments_assigned_stmts + + +@decorators.raise_if_nothing_inferred +def assign_assigned_stmts(self, node=None, context=None, assign_path=None): + if not assign_path: + yield self.value + return None + yield from _resolve_assignment_parts( + self.value.infer(context), assign_path, context + ) + + return dict(node=self, unknown=node, assign_path=assign_path, context=context) + + +def assign_annassigned_stmts(self, node=None, context=None, assign_path=None): + for inferred in assign_assigned_stmts(self, node, context, assign_path): + if inferred is None: + yield util.Uninferable + else: + yield inferred + + +nodes.Assign.assigned_stmts = assign_assigned_stmts +nodes.AnnAssign.assigned_stmts = assign_annassigned_stmts +nodes.AugAssign.assigned_stmts = assign_assigned_stmts + + +def _resolve_assignment_parts(parts, assign_path, context): + """recursive function to resolve multiple assignments""" + assign_path = assign_path[:] + index = assign_path.pop(0) + for part in parts: + assigned = None + if isinstance(part, nodes.Dict): + # A dictionary in an iterating context + try: + assigned, _ = part.items[index] + except IndexError: + return + + elif hasattr(part, "getitem"): + index_node = nodes.Const(index) + try: + assigned = part.getitem(index_node, context) + except (exceptions.AstroidTypeError, exceptions.AstroidIndexError): + return + + if not assigned: + return + + if not assign_path: + # we achieved to resolved the assignment path, don't infer the + # last part + yield assigned + elif assigned is util.Uninferable: + return + else: + # we are not yet on the last part of the path search on each + # possibly inferred value + try: + yield from _resolve_assignment_parts( + assigned.infer(context), assign_path, context + ) + except exceptions.InferenceError: + return + + +@decorators.raise_if_nothing_inferred +def excepthandler_assigned_stmts(self, node=None, context=None, assign_path=None): + for assigned in node_classes.unpack_infer(self.type): + if isinstance(assigned, nodes.ClassDef): + assigned = objects.ExceptionInstance(assigned) + + yield assigned + return dict(node=self, unknown=node, assign_path=assign_path, context=context) + + +nodes.ExceptHandler.assigned_stmts = excepthandler_assigned_stmts + + +def _infer_context_manager(self, mgr, context): + inferred = next(mgr.infer(context=context)) + if isinstance(inferred, bases.Generator): + # Check if it is decorated with contextlib.contextmanager. + func = inferred.parent + if not func.decorators: + raise exceptions.InferenceError( + "No decorators found on inferred generator %s", node=func + ) + + for decorator_node in func.decorators.nodes: + decorator = next(decorator_node.infer(context=context)) + if isinstance(decorator, nodes.FunctionDef): + if decorator.qname() == _CONTEXTLIB_MGR: + break + else: + # It doesn't interest us. + raise exceptions.InferenceError(node=func) + + # Get the first yield point. If it has multiple yields, + # then a RuntimeError will be raised. + + possible_yield_points = func.nodes_of_class(nodes.Yield) + # Ignore yields in nested functions + yield_point = next( + (node for node in possible_yield_points if node.scope() == func), None + ) + if yield_point: + if not yield_point.value: + const = nodes.Const(None) + const.parent = yield_point + const.lineno = yield_point.lineno + yield const + else: + yield from yield_point.value.infer(context=context) + elif isinstance(inferred, bases.Instance): + try: + enter = next(inferred.igetattr("__enter__", context=context)) + except (exceptions.InferenceError, exceptions.AttributeInferenceError): + raise exceptions.InferenceError(node=inferred) + if not isinstance(enter, bases.BoundMethod): + raise exceptions.InferenceError(node=enter) + yield from enter.infer_call_result(self, context) + else: + raise exceptions.InferenceError(node=mgr) + + +@decorators.raise_if_nothing_inferred +def with_assigned_stmts(self, node=None, context=None, assign_path=None): + """Infer names and other nodes from a *with* statement. + + This enables only inference for name binding in a *with* statement. + For instance, in the following code, inferring `func` will return + the `ContextManager` class, not whatever ``__enter__`` returns. + We are doing this intentionally, because we consider that the context + manager result is whatever __enter__ returns and what it is binded + using the ``as`` keyword. + + class ContextManager(object): + def __enter__(self): + return 42 + with ContextManager() as f: + pass + + # ContextManager().infer() will return ContextManager + # f.infer() will return 42. + + Arguments: + self: nodes.With + node: The target of the assignment, `as (a, b)` in `with foo as (a, b)`. + context: Inference context used for caching already inferred objects + assign_path: + A list of indices, where each index specifies what item to fetch from + the inference results. + """ + try: + mgr = next(mgr for (mgr, vars) in self.items if vars == node) + except StopIteration: + return None + if assign_path is None: + yield from _infer_context_manager(self, mgr, context) + else: + for result in _infer_context_manager(self, mgr, context): + # Walk the assign_path and get the item at the final index. + obj = result + for index in assign_path: + if not hasattr(obj, "elts"): + raise exceptions.InferenceError( + "Wrong type ({targets!r}) for {node!r} assignment", + node=self, + targets=node, + assign_path=assign_path, + context=context, + ) + try: + obj = obj.elts[index] + except IndexError as exc: + raise exceptions.InferenceError( + "Tried to infer a nonexistent target with index {index} " + "in {node!r}.", + node=self, + targets=node, + assign_path=assign_path, + context=context, + ) from exc + except TypeError as exc: + raise exceptions.InferenceError( + "Tried to unpack a non-iterable value " "in {node!r}.", + node=self, + targets=node, + assign_path=assign_path, + context=context, + ) from exc + yield obj + return dict(node=self, unknown=node, assign_path=assign_path, context=context) + + +nodes.With.assigned_stmts = with_assigned_stmts + + +@decorators.raise_if_nothing_inferred +def named_expr_assigned_stmts(self, node, context=None, assign_path=None): + """Infer names and other nodes from an assignment expression""" + if self.target == node: + yield from self.value.infer(context=context) + else: + raise exceptions.InferenceError( + "Cannot infer NamedExpr node {node!r}", + node=self, + assign_path=assign_path, + context=context, + ) + + +nodes.NamedExpr.assigned_stmts = named_expr_assigned_stmts + + +@decorators.yes_if_nothing_inferred +def starred_assigned_stmts(self, node=None, context=None, assign_path=None): + """ + Arguments: + self: nodes.Starred + node: a node related to the current underlying Node. + context: Inference context used for caching already inferred objects + assign_path: + A list of indices, where each index specifies what item to fetch from + the inference results. + """ + # pylint: disable=too-many-locals,too-many-branches,too-many-statements + def _determine_starred_iteration_lookups(starred, target, lookups): + # Determine the lookups for the rhs of the iteration + itered = target.itered() + for index, element in enumerate(itered): + if ( + isinstance(element, nodes.Starred) + and element.value.name == starred.value.name + ): + lookups.append((index, len(itered))) + break + if isinstance(element, nodes.Tuple): + lookups.append((index, len(element.itered()))) + _determine_starred_iteration_lookups(starred, element, lookups) + + stmt = self.statement() + if not isinstance(stmt, (nodes.Assign, nodes.For)): + raise exceptions.InferenceError( + "Statement {stmt!r} enclosing {node!r} " "must be an Assign or For node.", + node=self, + stmt=stmt, + unknown=node, + context=context, + ) + + if context is None: + context = contextmod.InferenceContext() + + if isinstance(stmt, nodes.Assign): + value = stmt.value + lhs = stmt.targets[0] + + if sum(1 for _ in lhs.nodes_of_class(nodes.Starred)) > 1: + raise exceptions.InferenceError( + "Too many starred arguments in the " " assignment targets {lhs!r}.", + node=self, + targets=lhs, + unknown=node, + context=context, + ) + + try: + rhs = next(value.infer(context)) + except exceptions.InferenceError: + yield util.Uninferable + return + if rhs is util.Uninferable or not hasattr(rhs, "itered"): + yield util.Uninferable + return + + try: + elts = collections.deque(rhs.itered()) + except TypeError: + yield util.Uninferable + return + + # Unpack iteratively the values from the rhs of the assignment, + # until the find the starred node. What will remain will + # be the list of values which the Starred node will represent + # This is done in two steps, from left to right to remove + # anything before the starred node and from right to left + # to remove anything after the starred node. + + for index, left_node in enumerate(lhs.elts): + if not isinstance(left_node, nodes.Starred): + if not elts: + break + elts.popleft() + continue + lhs_elts = collections.deque(reversed(lhs.elts[index:])) + for right_node in lhs_elts: + if not isinstance(right_node, nodes.Starred): + if not elts: + break + elts.pop() + continue + + # We're done unpacking. + elts = list(elts) + packed = nodes.List( + ctx=Store, parent=self, lineno=lhs.lineno, col_offset=lhs.col_offset + ) + packed.postinit(elts=elts) + yield packed + break + + if isinstance(stmt, nodes.For): + try: + inferred_iterable = next(stmt.iter.infer(context=context)) + except exceptions.InferenceError: + yield util.Uninferable + return + if inferred_iterable is util.Uninferable or not hasattr( + inferred_iterable, "itered" + ): + yield util.Uninferable + return + try: + itered = inferred_iterable.itered() + except TypeError: + yield util.Uninferable + return + + target = stmt.target + + if not isinstance(target, nodes.Tuple): + raise exceptions.InferenceError( + "Could not make sense of this, the target must be a tuple", + context=context, + ) + + lookups = [] + _determine_starred_iteration_lookups(self, target, lookups) + if not lookups: + raise exceptions.InferenceError( + "Could not make sense of this, needs at least a lookup", context=context + ) + + # Make the last lookup a slice, since that what we want for a Starred node + last_element_index, last_element_length = lookups[-1] + is_starred_last = last_element_index == (last_element_length - 1) + + lookup_slice = slice( + last_element_index, + None if is_starred_last else (last_element_length - last_element_index), + ) + lookups[-1] = lookup_slice + + for element in itered: + + # We probably want to infer the potential values *for each* element in an + # iterable, but we can't infer a list of all values, when only a list of + # step values are expected: + # + # for a, *b in [...]: + # b + # + # *b* should now point to just the elements at that particular iteration step, + # which astroid can't know about. + + found_element = None + for lookup in lookups: + if not hasattr(element, "itered"): + break + if not isinstance(lookup, slice): + # Grab just the index, not the whole length + lookup = lookup[0] + try: + itered_inner_element = element.itered() + element = itered_inner_element[lookup] + except IndexError: + break + except TypeError: + # Most likely the itered() call failed, cannot make sense of this + yield util.Uninferable + return + else: + found_element = element + + unpacked = nodes.List( + ctx=Store, parent=self, lineno=self.lineno, col_offset=self.col_offset + ) + unpacked.postinit(elts=found_element or []) + yield unpacked + return + + yield util.Uninferable + + +nodes.Starred.assigned_stmts = starred_assigned_stmts diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/raw_building.py b/Display/.venv/lib/python3.7/site-packages/astroid/raw_building.py new file mode 100644 index 0000000..b261277 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/raw_building.py @@ -0,0 +1,483 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2012 FELD Boris +# Copyright (c) 2014-2020 Claudiu Popa +# Copyright (c) 2014 Google, Inc. +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2015 Florian Bruhin +# Copyright (c) 2015 Ovidiu Sabou +# Copyright (c) 2016 Derek Gustafson +# Copyright (c) 2016 Jakub Wilk +# Copyright (c) 2018 Ville Skyttä +# Copyright (c) 2018 Nick Drozd +# Copyright (c) 2018 Bryce Guinta +# Copyright (c) 2020 Robin Jarry + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""this module contains a set of functions to create astroid trees from scratch +(build_* functions) or from living object (object_build_* functions) +""" + +import builtins +import inspect +import os +import sys +import types + +from astroid import bases +from astroid import manager +from astroid import node_classes +from astroid import nodes + + +MANAGER = manager.AstroidManager() +# the keys of CONST_CLS eg python builtin types + +_CONSTANTS = tuple(node_classes.CONST_CLS) +_BUILTINS = vars(builtins) +TYPE_NONE = type(None) +TYPE_NOTIMPLEMENTED = type(NotImplemented) +TYPE_ELLIPSIS = type(...) + + +def _io_discrepancy(member): + # _io module names itself `io`: http://bugs.python.org/issue18602 + member_self = getattr(member, "__self__", None) + return ( + member_self + and inspect.ismodule(member_self) + and member_self.__name__ == "_io" + and member.__module__ == "io" + ) + + +def _attach_local_node(parent, node, name): + node.name = name # needed by add_local_node + parent.add_local_node(node) + + +def _add_dunder_class(func, member): + """Add a __class__ member to the given func node, if we can determine it.""" + python_cls = member.__class__ + cls_name = getattr(python_cls, "__name__", None) + if not cls_name: + return + cls_bases = [ancestor.__name__ for ancestor in python_cls.__bases__] + ast_klass = build_class(cls_name, cls_bases, python_cls.__doc__) + func.instance_attrs["__class__"] = [ast_klass] + + +_marker = object() + + +def attach_dummy_node(node, name, runtime_object=_marker): + """create a dummy node and register it in the locals of the given + node with the specified name + """ + enode = nodes.EmptyNode() + enode.object = runtime_object + _attach_local_node(node, enode, name) + + +def _has_underlying_object(self): + return self.object is not None and self.object is not _marker + + +nodes.EmptyNode.has_underlying_object = _has_underlying_object + + +def attach_const_node(node, name, value): + """create a Const node and register it in the locals of the given + node with the specified name + """ + if name not in node.special_attributes: + _attach_local_node(node, nodes.const_factory(value), name) + + +def attach_import_node(node, modname, membername): + """create a ImportFrom node and register it in the locals of the given + node with the specified name + """ + from_node = nodes.ImportFrom(modname, [(membername, None)]) + _attach_local_node(node, from_node, membername) + + +def build_module(name, doc=None): + """create and initialize an astroid Module node""" + node = nodes.Module(name, doc, pure_python=False) + node.package = False + node.parent = None + return node + + +def build_class(name, basenames=(), doc=None): + """create and initialize an astroid ClassDef node""" + node = nodes.ClassDef(name, doc) + for base in basenames: + basenode = nodes.Name() + basenode.name = base + node.bases.append(basenode) + basenode.parent = node + return node + + +def build_function(name, args=None, posonlyargs=None, defaults=None, doc=None): + """create and initialize an astroid FunctionDef node""" + args, defaults, posonlyargs = args or [], defaults or [], posonlyargs or [] + # first argument is now a list of decorators + func = nodes.FunctionDef(name, doc) + func.args = argsnode = nodes.Arguments() + argsnode.args = [] + argsnode.posonlyargs = [] + for arg in args: + argsnode.args.append(nodes.Name()) + argsnode.args[-1].name = arg + argsnode.args[-1].parent = argsnode + for arg in posonlyargs: + argsnode.posonlyargs.append(nodes.Name()) + argsnode.posonlyargs[-1].name = arg + argsnode.posonlyargs[-1].parent = argsnode + argsnode.defaults = [] + for default in defaults: + argsnode.defaults.append(nodes.const_factory(default)) + argsnode.defaults[-1].parent = argsnode + argsnode.kwarg = None + argsnode.vararg = None + argsnode.parent = func + if args: + register_arguments(func) + return func + + +def build_from_import(fromname, names): + """create and initialize an astroid ImportFrom import statement""" + return nodes.ImportFrom(fromname, [(name, None) for name in names]) + + +def register_arguments(func, args=None): + """add given arguments to local + + args is a list that may contains nested lists + (i.e. def func(a, (b, c, d)): ...) + """ + if args is None: + args = func.args.args + if func.args.vararg: + func.set_local(func.args.vararg, func.args) + if func.args.kwarg: + func.set_local(func.args.kwarg, func.args) + for arg in args: + if isinstance(arg, nodes.Name): + func.set_local(arg.name, arg) + else: + register_arguments(func, arg.elts) + + +def object_build_class(node, member, localname): + """create astroid for a living class object""" + basenames = [base.__name__ for base in member.__bases__] + return _base_class_object_build(node, member, basenames, localname=localname) + + +def object_build_function(node, member, localname): + """create astroid for a living function object""" + signature = inspect.signature(member) + args = [] + defaults = [] + posonlyargs = [] + for param_name, param in signature.parameters.items(): + if param.kind == inspect.Parameter.POSITIONAL_ONLY: + posonlyargs.append(param_name) + elif param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD: + args.append(param_name) + elif param.kind == inspect.Parameter.VAR_POSITIONAL: + args.append(param_name) + elif param.kind == inspect.Parameter.VAR_KEYWORD: + args.append(param_name) + if param.default is not inspect._empty: + defaults.append(param.default) + func = build_function( + getattr(member, "__name__", None) or localname, + args, + posonlyargs, + defaults, + member.__doc__, + ) + node.add_local_node(func, localname) + + +def object_build_datadescriptor(node, member, name): + """create astroid for a living data descriptor object""" + return _base_class_object_build(node, member, [], name) + + +def object_build_methoddescriptor(node, member, localname): + """create astroid for a living method descriptor object""" + # FIXME get arguments ? + func = build_function( + getattr(member, "__name__", None) or localname, doc=member.__doc__ + ) + # set node's arguments to None to notice that we have no information, not + # and empty argument list + func.args.args = None + node.add_local_node(func, localname) + _add_dunder_class(func, member) + + +def _base_class_object_build(node, member, basenames, name=None, localname=None): + """create astroid for a living class object, with a given set of base names + (e.g. ancestors) + """ + klass = build_class( + name or getattr(member, "__name__", None) or localname, + basenames, + member.__doc__, + ) + klass._newstyle = isinstance(member, type) + node.add_local_node(klass, localname) + try: + # limit the instantiation trick since it's too dangerous + # (such as infinite test execution...) + # this at least resolves common case such as Exception.args, + # OSError.errno + if issubclass(member, Exception): + instdict = member().__dict__ + else: + raise TypeError + except TypeError: + pass + else: + for item_name, obj in instdict.items(): + valnode = nodes.EmptyNode() + valnode.object = obj + valnode.parent = klass + valnode.lineno = 1 + klass.instance_attrs[item_name] = [valnode] + return klass + + +def _build_from_function(node, name, member, module): + # verify this is not an imported function + try: + code = member.__code__ + except AttributeError: + # Some implementations don't provide the code object, + # such as Jython. + code = None + filename = getattr(code, "co_filename", None) + if filename is None: + assert isinstance(member, object) + object_build_methoddescriptor(node, member, name) + elif filename != getattr(module, "__file__", None): + attach_dummy_node(node, name, member) + else: + object_build_function(node, member, name) + + +def _safe_has_attribute(obj, member): + try: + return hasattr(obj, member) + except Exception: # pylint: disable=broad-except + return False + + +class InspectBuilder: + """class for building nodes from living object + + this is actually a really minimal representation, including only Module, + FunctionDef and ClassDef nodes and some others as guessed. + """ + + def __init__(self): + self._done = {} + self._module = None + + def inspect_build(self, module, modname=None, path=None): + """build astroid from a living module (i.e. using inspect) + this is used when there is no python source code available (either + because it's a built-in module or because the .py is not available) + """ + self._module = module + if modname is None: + modname = module.__name__ + try: + node = build_module(modname, module.__doc__) + except AttributeError: + # in jython, java modules have no __doc__ (see #109562) + node = build_module(modname) + node.file = node.path = os.path.abspath(path) if path else path + node.name = modname + MANAGER.cache_module(node) + node.package = hasattr(module, "__path__") + self._done = {} + self.object_build(node, module) + return node + + def object_build(self, node, obj): + """recursive method which create a partial ast from real objects + (only function, class, and method are handled) + """ + if obj in self._done: + return self._done[obj] + self._done[obj] = node + for name in dir(obj): + try: + member = getattr(obj, name) + except AttributeError: + # damned ExtensionClass.Base, I know you're there ! + attach_dummy_node(node, name) + continue + if inspect.ismethod(member): + member = member.__func__ + if inspect.isfunction(member): + _build_from_function(node, name, member, self._module) + elif inspect.isbuiltin(member): + if not _io_discrepancy(member) and self.imported_member( + node, member, name + ): + continue + object_build_methoddescriptor(node, member, name) + elif inspect.isclass(member): + if self.imported_member(node, member, name): + continue + if member in self._done: + class_node = self._done[member] + if class_node not in node.locals.get(name, ()): + node.add_local_node(class_node, name) + else: + class_node = object_build_class(node, member, name) + # recursion + self.object_build(class_node, member) + if name == "__class__" and class_node.parent is None: + class_node.parent = self._done[self._module] + elif inspect.ismethoddescriptor(member): + assert isinstance(member, object) + object_build_methoddescriptor(node, member, name) + elif inspect.isdatadescriptor(member): + assert isinstance(member, object) + object_build_datadescriptor(node, member, name) + elif isinstance(member, _CONSTANTS): + attach_const_node(node, name, member) + elif inspect.isroutine(member): + # This should be called for Jython, where some builtin + # methods aren't caught by isbuiltin branch. + _build_from_function(node, name, member, self._module) + elif _safe_has_attribute(member, "__all__"): + module = build_module(name) + _attach_local_node(node, module, name) + # recursion + self.object_build(module, member) + else: + # create an empty node so that the name is actually defined + attach_dummy_node(node, name, member) + return None + + def imported_member(self, node, member, name): + """verify this is not an imported class or handle it""" + # /!\ some classes like ExtensionClass doesn't have a __module__ + # attribute ! Also, this may trigger an exception on badly built module + # (see http://www.logilab.org/ticket/57299 for instance) + try: + modname = getattr(member, "__module__", None) + except TypeError: + modname = None + if modname is None: + if name in ("__new__", "__subclasshook__"): + # Python 2.5.1 (r251:54863, Sep 1 2010, 22:03:14) + # >>> print object.__new__.__module__ + # None + modname = builtins.__name__ + else: + attach_dummy_node(node, name, member) + return True + + real_name = {"gtk": "gtk_gtk", "_io": "io"}.get(modname, modname) + + if real_name != self._module.__name__: + # check if it sounds valid and then add an import node, else use a + # dummy node + try: + getattr(sys.modules[modname], name) + except (KeyError, AttributeError): + attach_dummy_node(node, name, member) + else: + attach_import_node(node, modname, name) + return True + return False + + +### astroid bootstrapping ###################################################### + +_CONST_PROXY = {} + +# TODO : find a nicer way to handle this situation; +def _set_proxied(const): + return _CONST_PROXY[const.value.__class__] + + +def _astroid_bootstrapping(): + """astroid bootstrapping the builtins module""" + # this boot strapping is necessary since we need the Const nodes to + # inspect_build builtins, and then we can proxy Const + builder = InspectBuilder() + astroid_builtin = builder.inspect_build(builtins) + + # pylint: disable=redefined-outer-name + for cls, node_cls in node_classes.CONST_CLS.items(): + if cls is TYPE_NONE: + proxy = build_class("NoneType") + proxy.parent = astroid_builtin + elif cls is TYPE_NOTIMPLEMENTED: + proxy = build_class("NotImplementedType") + proxy.parent = astroid_builtin + elif cls is TYPE_ELLIPSIS: + proxy = build_class("Ellipsis") + proxy.parent = astroid_builtin + else: + proxy = astroid_builtin.getattr(cls.__name__)[0] + if cls in (dict, list, set, tuple): + node_cls._proxied = proxy + else: + _CONST_PROXY[cls] = proxy + + # Set the builtin module as parent for some builtins. + nodes.Const._proxied = property(_set_proxied) + + _GeneratorType = nodes.ClassDef( + types.GeneratorType.__name__, types.GeneratorType.__doc__ + ) + _GeneratorType.parent = astroid_builtin + bases.Generator._proxied = _GeneratorType + builder.object_build(bases.Generator._proxied, types.GeneratorType) + + if hasattr(types, "AsyncGeneratorType"): + # pylint: disable=no-member; AsyncGeneratorType + _AsyncGeneratorType = nodes.ClassDef( + types.AsyncGeneratorType.__name__, types.AsyncGeneratorType.__doc__ + ) + _AsyncGeneratorType.parent = astroid_builtin + bases.AsyncGenerator._proxied = _AsyncGeneratorType + builder.object_build(bases.AsyncGenerator._proxied, types.AsyncGeneratorType) + builtin_types = ( + types.GetSetDescriptorType, + types.GeneratorType, + types.MemberDescriptorType, + TYPE_NONE, + TYPE_NOTIMPLEMENTED, + types.FunctionType, + types.MethodType, + types.BuiltinFunctionType, + types.ModuleType, + types.TracebackType, + ) + for _type in builtin_types: + if _type.__name__ not in astroid_builtin: + cls = nodes.ClassDef(_type.__name__, _type.__doc__) + cls.parent = astroid_builtin + builder.object_build(cls, _type) + astroid_builtin[_type.__name__] = cls + + +_astroid_bootstrapping() diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/rebuilder.py b/Display/.venv/lib/python3.7/site-packages/astroid/rebuilder.py new file mode 100644 index 0000000..3fc1a83 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/rebuilder.py @@ -0,0 +1,1010 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2009-2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2013-2020 Claudiu Popa +# Copyright (c) 2013-2014 Google, Inc. +# Copyright (c) 2014 Alexander Presnyakov +# Copyright (c) 2014 Eevee (Alex Munroe) +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2016-2017 Derek Gustafson +# Copyright (c) 2016 Jared Garst +# Copyright (c) 2017 Hugo +# Copyright (c) 2017 Łukasz Rogalski +# Copyright (c) 2017 rr- +# Copyright (c) 2018-2019 Ville Skyttä +# Copyright (c) 2018 Tomas Gavenciak +# Copyright (c) 2018 Serhiy Storchaka +# Copyright (c) 2018 Nick Drozd +# Copyright (c) 2018 Bryce Guinta +# Copyright (c) 2019-2020 Ashley Whetter +# Copyright (c) 2019 Hugo van Kemenade +# Copyright (c) 2019 Zbigniew Jędrzejewski-Szmek + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""this module contains utilities for rebuilding an _ast tree in +order to get a single Astroid representation +""" + +import sys +from typing import Optional + +import astroid +from astroid._ast import parse_function_type_comment, get_parser_module, ParserModule +from astroid import nodes + + +CONST_NAME_TRANSFORMS = {"None": None, "True": True, "False": False} + +REDIRECT = { + "arguments": "Arguments", + "comprehension": "Comprehension", + "ListCompFor": "Comprehension", + "GenExprFor": "Comprehension", + "excepthandler": "ExceptHandler", + "keyword": "Keyword", +} +PY37 = sys.version_info >= (3, 7) +PY38 = sys.version_info >= (3, 8) + + +def _visit_or_none(node, attr, visitor, parent, visit="visit", **kws): + """If the given node has an attribute, visits the attribute, and + otherwise returns None. + + """ + value = getattr(node, attr, None) + if value: + return getattr(visitor, visit)(value, parent, **kws) + + return None + + +class TreeRebuilder: + """Rebuilds the _ast tree to become an Astroid tree""" + + def __init__(self, manager, parser_module: Optional[ParserModule] = None): + self._manager = manager + self._global_names = [] + self._import_from_nodes = [] + self._delayed_assattr = [] + self._visit_meths = {} + + if parser_module is None: + self._parser_module = get_parser_module() + else: + self._parser_module = parser_module + self._module = self._parser_module.module + + def _get_doc(self, node): + try: + if PY37 and hasattr(node, "docstring"): + doc = node.docstring + return node, doc + if node.body and isinstance(node.body[0], self._module.Expr): + + first_value = node.body[0].value + if isinstance(first_value, self._module.Str) or ( + PY38 + and isinstance(first_value, self._module.Constant) + and isinstance(first_value.value, str) + ): + doc = first_value.value if PY38 else first_value.s + node.body = node.body[1:] + return node, doc + except IndexError: + pass # ast built from scratch + return node, None + + def _get_context(self, node): + return self._parser_module.context_classes.get(type(node.ctx), astroid.Load) + + def visit_module(self, node, modname, modpath, package): + """visit a Module node by returning a fresh instance of it""" + node, doc = self._get_doc(node) + newnode = nodes.Module( + name=modname, + doc=doc, + file=modpath, + path=[modpath], + package=package, + parent=None, + ) + newnode.postinit([self.visit(child, newnode) for child in node.body]) + return newnode + + def visit(self, node, parent): + cls = node.__class__ + if cls in self._visit_meths: + visit_method = self._visit_meths[cls] + else: + cls_name = cls.__name__ + visit_name = "visit_" + REDIRECT.get(cls_name, cls_name).lower() + visit_method = getattr(self, visit_name) + self._visit_meths[cls] = visit_method + return visit_method(node, parent) + + def _save_assignment(self, node, name=None): + """save assignement situation since node.parent is not available yet""" + if self._global_names and node.name in self._global_names[-1]: + node.root().set_local(node.name, node) + else: + node.parent.set_local(node.name, node) + + def visit_arg(self, node, parent): + """visit an arg node by returning a fresh AssName instance""" + return self.visit_assignname(node, parent, node.arg) + + def visit_arguments(self, node, parent): + """visit an Arguments node by returning a fresh instance of it""" + vararg, kwarg = node.vararg, node.kwarg + newnode = nodes.Arguments( + vararg.arg if vararg else None, kwarg.arg if kwarg else None, parent + ) + args = [self.visit(child, newnode) for child in node.args] + defaults = [self.visit(child, newnode) for child in node.defaults] + varargannotation = None + kwargannotation = None + posonlyargs = [] + # change added in 82732 (7c5c678e4164), vararg and kwarg + # are instances of `_ast.arg`, not strings + if vararg: + if node.vararg.annotation: + varargannotation = self.visit(node.vararg.annotation, newnode) + vararg = vararg.arg + if kwarg: + if node.kwarg.annotation: + kwargannotation = self.visit(node.kwarg.annotation, newnode) + kwarg = kwarg.arg + kwonlyargs = [self.visit(child, newnode) for child in node.kwonlyargs] + kw_defaults = [ + self.visit(child, newnode) if child else None for child in node.kw_defaults + ] + annotations = [ + self.visit(arg.annotation, newnode) if arg.annotation else None + for arg in node.args + ] + kwonlyargs_annotations = [ + self.visit(arg.annotation, newnode) if arg.annotation else None + for arg in node.kwonlyargs + ] + + posonlyargs_annotations = [] + if PY38: + posonlyargs = [self.visit(child, newnode) for child in node.posonlyargs] + posonlyargs_annotations = [ + self.visit(arg.annotation, newnode) if arg.annotation else None + for arg in node.posonlyargs + ] + type_comment_args = [ + self.check_type_comment(child, parent=newnode) for child in node.args + ] + type_comment_kwonlyargs = [ + self.check_type_comment(child, parent=newnode) for child in node.kwonlyargs + ] + type_comment_posonlyargs = [] + if PY38: + type_comment_posonlyargs = [ + self.check_type_comment(child, parent=newnode) + for child in node.posonlyargs + ] + + newnode.postinit( + args=args, + defaults=defaults, + kwonlyargs=kwonlyargs, + posonlyargs=posonlyargs, + kw_defaults=kw_defaults, + annotations=annotations, + kwonlyargs_annotations=kwonlyargs_annotations, + posonlyargs_annotations=posonlyargs_annotations, + varargannotation=varargannotation, + kwargannotation=kwargannotation, + type_comment_args=type_comment_args, + type_comment_kwonlyargs=type_comment_kwonlyargs, + type_comment_posonlyargs=type_comment_posonlyargs, + ) + # save argument names in locals: + if vararg: + newnode.parent.set_local(vararg, newnode) + if kwarg: + newnode.parent.set_local(kwarg, newnode) + return newnode + + def visit_assert(self, node, parent): + """visit a Assert node by returning a fresh instance of it""" + newnode = nodes.Assert(node.lineno, node.col_offset, parent) + if node.msg: + msg = self.visit(node.msg, newnode) + else: + msg = None + newnode.postinit(self.visit(node.test, newnode), msg) + return newnode + + def check_type_comment(self, node, parent): + type_comment = getattr(node, "type_comment", None) + if not type_comment: + return None + + try: + type_comment_ast = self._parser_module.parse(type_comment) + except SyntaxError: + # Invalid type comment, just skip it. + return None + + type_object = self.visit(type_comment_ast.body[0], parent=parent) + if not isinstance(type_object, nodes.Expr): + return None + + return type_object.value + + def check_function_type_comment(self, node): + type_comment = getattr(node, "type_comment", None) + if not type_comment: + return None + + try: + type_comment_ast = parse_function_type_comment(type_comment) + except SyntaxError: + # Invalid type comment, just skip it. + return None + + returns = None + argtypes = [ + self.visit(elem, node) for elem in (type_comment_ast.argtypes or []) + ] + if type_comment_ast.returns: + returns = self.visit(type_comment_ast.returns, node) + + return returns, argtypes + + # Async structs added in Python 3.5 + def visit_asyncfunctiondef(self, node, parent): + return self._visit_functiondef(nodes.AsyncFunctionDef, node, parent) + + def visit_asyncfor(self, node, parent): + return self._visit_for(nodes.AsyncFor, node, parent) + + def visit_await(self, node, parent): + newnode = nodes.Await(node.lineno, node.col_offset, parent) + newnode.postinit(value=self.visit(node.value, newnode)) + return newnode + + def visit_asyncwith(self, node, parent): + return self._visit_with(nodes.AsyncWith, node, parent) + + def visit_assign(self, node, parent): + """visit a Assign node by returning a fresh instance of it""" + newnode = nodes.Assign(node.lineno, node.col_offset, parent) + type_annotation = self.check_type_comment(node, parent=newnode) + newnode.postinit( + targets=[self.visit(child, newnode) for child in node.targets], + value=self.visit(node.value, newnode), + type_annotation=type_annotation, + ) + return newnode + + def visit_annassign(self, node, parent): + """visit an AnnAssign node by returning a fresh instance of it""" + newnode = nodes.AnnAssign(node.lineno, node.col_offset, parent) + annotation = _visit_or_none(node, "annotation", self, newnode) + newnode.postinit( + target=self.visit(node.target, newnode), + annotation=annotation, + simple=node.simple, + value=_visit_or_none(node, "value", self, newnode), + ) + return newnode + + def visit_assignname(self, node, parent, node_name=None): + """visit a node and return a AssignName node""" + newnode = nodes.AssignName( + node_name, + getattr(node, "lineno", None), + getattr(node, "col_offset", None), + parent, + ) + self._save_assignment(newnode) + return newnode + + def visit_augassign(self, node, parent): + """visit a AugAssign node by returning a fresh instance of it""" + newnode = nodes.AugAssign( + self._parser_module.bin_op_classes[type(node.op)] + "=", + node.lineno, + node.col_offset, + parent, + ) + newnode.postinit( + self.visit(node.target, newnode), self.visit(node.value, newnode) + ) + return newnode + + def visit_repr(self, node, parent): + """visit a Backquote node by returning a fresh instance of it""" + newnode = nodes.Repr(node.lineno, node.col_offset, parent) + newnode.postinit(self.visit(node.value, newnode)) + return newnode + + def visit_binop(self, node, parent): + """visit a BinOp node by returning a fresh instance of it""" + newnode = nodes.BinOp( + self._parser_module.bin_op_classes[type(node.op)], + node.lineno, + node.col_offset, + parent, + ) + newnode.postinit( + self.visit(node.left, newnode), self.visit(node.right, newnode) + ) + return newnode + + def visit_boolop(self, node, parent): + """visit a BoolOp node by returning a fresh instance of it""" + newnode = nodes.BoolOp( + self._parser_module.bool_op_classes[type(node.op)], + node.lineno, + node.col_offset, + parent, + ) + newnode.postinit([self.visit(child, newnode) for child in node.values]) + return newnode + + def visit_break(self, node, parent): + """visit a Break node by returning a fresh instance of it""" + return nodes.Break( + getattr(node, "lineno", None), getattr(node, "col_offset", None), parent + ) + + def visit_call(self, node, parent): + """visit a CallFunc node by returning a fresh instance of it""" + newnode = nodes.Call(node.lineno, node.col_offset, parent) + starargs = _visit_or_none(node, "starargs", self, newnode) + kwargs = _visit_or_none(node, "kwargs", self, newnode) + args = [self.visit(child, newnode) for child in node.args] + + if node.keywords: + keywords = [self.visit(child, newnode) for child in node.keywords] + else: + keywords = None + if starargs: + new_starargs = nodes.Starred( + col_offset=starargs.col_offset, + lineno=starargs.lineno, + parent=starargs.parent, + ) + new_starargs.postinit(value=starargs) + args.append(new_starargs) + if kwargs: + new_kwargs = nodes.Keyword( + arg=None, + col_offset=kwargs.col_offset, + lineno=kwargs.lineno, + parent=kwargs.parent, + ) + new_kwargs.postinit(value=kwargs) + if keywords: + keywords.append(new_kwargs) + else: + keywords = [new_kwargs] + + newnode.postinit(self.visit(node.func, newnode), args, keywords) + return newnode + + def visit_classdef(self, node, parent, newstyle=True): + """visit a ClassDef node to become astroid""" + node, doc = self._get_doc(node) + newnode = nodes.ClassDef(node.name, doc, node.lineno, node.col_offset, parent) + metaclass = None + for keyword in node.keywords: + if keyword.arg == "metaclass": + metaclass = self.visit(keyword, newnode).value + break + if node.decorator_list: + decorators = self.visit_decorators(node, newnode) + else: + decorators = None + newnode.postinit( + [self.visit(child, newnode) for child in node.bases], + [self.visit(child, newnode) for child in node.body], + decorators, + newstyle, + metaclass, + [ + self.visit(kwd, newnode) + for kwd in node.keywords + if kwd.arg != "metaclass" + ], + ) + return newnode + + def visit_const(self, node, parent): + """visit a Const node by returning a fresh instance of it""" + return nodes.Const( + node.value, + getattr(node, "lineno", None), + getattr(node, "col_offset", None), + parent, + ) + + def visit_continue(self, node, parent): + """visit a Continue node by returning a fresh instance of it""" + return nodes.Continue( + getattr(node, "lineno", None), getattr(node, "col_offset", None), parent + ) + + def visit_compare(self, node, parent): + """visit a Compare node by returning a fresh instance of it""" + newnode = nodes.Compare(node.lineno, node.col_offset, parent) + newnode.postinit( + self.visit(node.left, newnode), + [ + ( + self._parser_module.cmp_op_classes[op.__class__], + self.visit(expr, newnode), + ) + for (op, expr) in zip(node.ops, node.comparators) + ], + ) + return newnode + + def visit_comprehension(self, node, parent): + """visit a Comprehension node by returning a fresh instance of it""" + newnode = nodes.Comprehension(parent) + newnode.postinit( + self.visit(node.target, newnode), + self.visit(node.iter, newnode), + [self.visit(child, newnode) for child in node.ifs], + getattr(node, "is_async", None), + ) + return newnode + + def visit_decorators(self, node, parent): + """visit a Decorators node by returning a fresh instance of it""" + # /!\ node is actually an _ast.FunctionDef node while + # parent is an astroid.nodes.FunctionDef node + if PY38: + # Set the line number of the first decorator for Python 3.8+. + lineno = node.decorator_list[0].lineno + else: + lineno = node.lineno + newnode = nodes.Decorators(lineno, node.col_offset, parent) + newnode.postinit([self.visit(child, newnode) for child in node.decorator_list]) + return newnode + + def visit_delete(self, node, parent): + """visit a Delete node by returning a fresh instance of it""" + newnode = nodes.Delete(node.lineno, node.col_offset, parent) + newnode.postinit([self.visit(child, newnode) for child in node.targets]) + return newnode + + def _visit_dict_items(self, node, parent, newnode): + for key, value in zip(node.keys, node.values): + rebuilt_value = self.visit(value, newnode) + if not key: + # Python 3.5 and extended unpacking + rebuilt_key = nodes.DictUnpack( + rebuilt_value.lineno, rebuilt_value.col_offset, parent + ) + else: + rebuilt_key = self.visit(key, newnode) + yield rebuilt_key, rebuilt_value + + def visit_dict(self, node, parent): + """visit a Dict node by returning a fresh instance of it""" + newnode = nodes.Dict(node.lineno, node.col_offset, parent) + items = list(self._visit_dict_items(node, parent, newnode)) + newnode.postinit(items) + return newnode + + def visit_dictcomp(self, node, parent): + """visit a DictComp node by returning a fresh instance of it""" + newnode = nodes.DictComp(node.lineno, node.col_offset, parent) + newnode.postinit( + self.visit(node.key, newnode), + self.visit(node.value, newnode), + [self.visit(child, newnode) for child in node.generators], + ) + return newnode + + def visit_expr(self, node, parent): + """visit a Expr node by returning a fresh instance of it""" + newnode = nodes.Expr(node.lineno, node.col_offset, parent) + newnode.postinit(self.visit(node.value, newnode)) + return newnode + + # Not used in Python 3.8+. + def visit_ellipsis(self, node, parent): + """visit an Ellipsis node by returning a fresh instance of it""" + return nodes.Ellipsis( + getattr(node, "lineno", None), getattr(node, "col_offset", None), parent + ) + + def visit_emptynode(self, node, parent): + """visit an EmptyNode node by returning a fresh instance of it""" + return nodes.EmptyNode( + getattr(node, "lineno", None), getattr(node, "col_offset", None), parent + ) + + def visit_excepthandler(self, node, parent): + """visit an ExceptHandler node by returning a fresh instance of it""" + newnode = nodes.ExceptHandler(node.lineno, node.col_offset, parent) + if node.name: + name = self.visit_assignname(node, newnode, node.name) + else: + name = None + newnode.postinit( + _visit_or_none(node, "type", self, newnode), + name, + [self.visit(child, newnode) for child in node.body], + ) + return newnode + + def visit_exec(self, node, parent): + """visit an Exec node by returning a fresh instance of it""" + newnode = nodes.Exec(node.lineno, node.col_offset, parent) + newnode.postinit( + self.visit(node.body, newnode), + _visit_or_none(node, "globals", self, newnode), + _visit_or_none(node, "locals", self, newnode), + ) + return newnode + + # Not used in Python 3.8+. + def visit_extslice(self, node, parent): + """visit an ExtSlice node by returning a fresh instance of it""" + newnode = nodes.ExtSlice(parent=parent) + newnode.postinit([self.visit(dim, newnode) for dim in node.dims]) + return newnode + + def _visit_for(self, cls, node, parent): + """visit a For node by returning a fresh instance of it""" + newnode = cls(node.lineno, node.col_offset, parent) + type_annotation = self.check_type_comment(node, parent=newnode) + newnode.postinit( + target=self.visit(node.target, newnode), + iter=self.visit(node.iter, newnode), + body=[self.visit(child, newnode) for child in node.body], + orelse=[self.visit(child, newnode) for child in node.orelse], + type_annotation=type_annotation, + ) + return newnode + + def visit_for(self, node, parent): + return self._visit_for(nodes.For, node, parent) + + def visit_importfrom(self, node, parent): + """visit an ImportFrom node by returning a fresh instance of it""" + names = [(alias.name, alias.asname) for alias in node.names] + newnode = nodes.ImportFrom( + node.module or "", + names, + node.level or None, + getattr(node, "lineno", None), + getattr(node, "col_offset", None), + parent, + ) + # store From names to add them to locals after building + self._import_from_nodes.append(newnode) + return newnode + + def _visit_functiondef(self, cls, node, parent): + """visit an FunctionDef node to become astroid""" + self._global_names.append({}) + node, doc = self._get_doc(node) + + lineno = node.lineno + if PY38 and node.decorator_list: + # Python 3.8 sets the line number of a decorated function + # to be the actual line number of the function, but the + # previous versions expected the decorator's line number instead. + # We reset the function's line number to that of the + # first decorator to maintain backward compatibility. + # It's not ideal but this discrepancy was baked into + # the framework for *years*. + lineno = node.decorator_list[0].lineno + + newnode = cls(node.name, doc, lineno, node.col_offset, parent) + if node.decorator_list: + decorators = self.visit_decorators(node, newnode) + else: + decorators = None + if node.returns: + returns = self.visit(node.returns, newnode) + else: + returns = None + + type_comment_args = type_comment_returns = None + type_comment_annotation = self.check_function_type_comment(node) + if type_comment_annotation: + type_comment_returns, type_comment_args = type_comment_annotation + newnode.postinit( + args=self.visit(node.args, newnode), + body=[self.visit(child, newnode) for child in node.body], + decorators=decorators, + returns=returns, + type_comment_returns=type_comment_returns, + type_comment_args=type_comment_args, + ) + self._global_names.pop() + return newnode + + def visit_functiondef(self, node, parent): + return self._visit_functiondef(nodes.FunctionDef, node, parent) + + def visit_generatorexp(self, node, parent): + """visit a GeneratorExp node by returning a fresh instance of it""" + newnode = nodes.GeneratorExp(node.lineno, node.col_offset, parent) + newnode.postinit( + self.visit(node.elt, newnode), + [self.visit(child, newnode) for child in node.generators], + ) + return newnode + + def visit_attribute(self, node, parent): + """visit an Attribute node by returning a fresh instance of it""" + context = self._get_context(node) + if context == astroid.Del: + # FIXME : maybe we should reintroduce and visit_delattr ? + # for instance, deactivating assign_ctx + newnode = nodes.DelAttr(node.attr, node.lineno, node.col_offset, parent) + elif context == astroid.Store: + newnode = nodes.AssignAttr(node.attr, node.lineno, node.col_offset, parent) + # Prohibit a local save if we are in an ExceptHandler. + if not isinstance(parent, astroid.ExceptHandler): + self._delayed_assattr.append(newnode) + else: + newnode = nodes.Attribute(node.attr, node.lineno, node.col_offset, parent) + newnode.postinit(self.visit(node.value, newnode)) + return newnode + + def visit_global(self, node, parent): + """visit a Global node to become astroid""" + newnode = nodes.Global( + node.names, + getattr(node, "lineno", None), + getattr(node, "col_offset", None), + parent, + ) + if self._global_names: # global at the module level, no effect + for name in node.names: + self._global_names[-1].setdefault(name, []).append(newnode) + return newnode + + def visit_if(self, node, parent): + """visit an If node by returning a fresh instance of it""" + newnode = nodes.If(node.lineno, node.col_offset, parent) + newnode.postinit( + self.visit(node.test, newnode), + [self.visit(child, newnode) for child in node.body], + [self.visit(child, newnode) for child in node.orelse], + ) + return newnode + + def visit_ifexp(self, node, parent): + """visit a IfExp node by returning a fresh instance of it""" + newnode = nodes.IfExp(node.lineno, node.col_offset, parent) + newnode.postinit( + self.visit(node.test, newnode), + self.visit(node.body, newnode), + self.visit(node.orelse, newnode), + ) + return newnode + + def visit_import(self, node, parent): + """visit a Import node by returning a fresh instance of it""" + names = [(alias.name, alias.asname) for alias in node.names] + newnode = nodes.Import( + names, + getattr(node, "lineno", None), + getattr(node, "col_offset", None), + parent, + ) + # save import names in parent's locals: + for (name, asname) in newnode.names: + name = asname or name + parent.set_local(name.split(".")[0], newnode) + return newnode + + def visit_joinedstr(self, node, parent): + newnode = nodes.JoinedStr(node.lineno, node.col_offset, parent) + newnode.postinit([self.visit(child, newnode) for child in node.values]) + return newnode + + def visit_formattedvalue(self, node, parent): + newnode = nodes.FormattedValue(node.lineno, node.col_offset, parent) + newnode.postinit( + self.visit(node.value, newnode), + node.conversion, + _visit_or_none(node, "format_spec", self, newnode), + ) + return newnode + + def visit_namedexpr(self, node, parent): + newnode = nodes.NamedExpr(node.lineno, node.col_offset, parent) + newnode.postinit( + self.visit(node.target, newnode), self.visit(node.value, newnode) + ) + return newnode + + # Not used in Python 3.8+. + def visit_index(self, node, parent): + """visit a Index node by returning a fresh instance of it""" + newnode = nodes.Index(parent=parent) + newnode.postinit(self.visit(node.value, newnode)) + return newnode + + def visit_keyword(self, node, parent): + """visit a Keyword node by returning a fresh instance of it""" + newnode = nodes.Keyword(node.arg, parent=parent) + newnode.postinit(self.visit(node.value, newnode)) + return newnode + + def visit_lambda(self, node, parent): + """visit a Lambda node by returning a fresh instance of it""" + newnode = nodes.Lambda(node.lineno, node.col_offset, parent) + newnode.postinit(self.visit(node.args, newnode), self.visit(node.body, newnode)) + return newnode + + def visit_list(self, node, parent): + """visit a List node by returning a fresh instance of it""" + context = self._get_context(node) + newnode = nodes.List( + ctx=context, lineno=node.lineno, col_offset=node.col_offset, parent=parent + ) + newnode.postinit([self.visit(child, newnode) for child in node.elts]) + return newnode + + def visit_listcomp(self, node, parent): + """visit a ListComp node by returning a fresh instance of it""" + newnode = nodes.ListComp(node.lineno, node.col_offset, parent) + newnode.postinit( + self.visit(node.elt, newnode), + [self.visit(child, newnode) for child in node.generators], + ) + return newnode + + def visit_name(self, node, parent): + """visit a Name node by returning a fresh instance of it""" + context = self._get_context(node) + # True and False can be assigned to something in py2x, so we have to + # check first the context. + if context == astroid.Del: + newnode = nodes.DelName(node.id, node.lineno, node.col_offset, parent) + elif context == astroid.Store: + newnode = nodes.AssignName(node.id, node.lineno, node.col_offset, parent) + elif node.id in CONST_NAME_TRANSFORMS: + newnode = nodes.Const( + CONST_NAME_TRANSFORMS[node.id], + getattr(node, "lineno", None), + getattr(node, "col_offset", None), + parent, + ) + return newnode + else: + newnode = nodes.Name(node.id, node.lineno, node.col_offset, parent) + # XXX REMOVE me : + if context in (astroid.Del, astroid.Store): # 'Aug' ?? + self._save_assignment(newnode) + return newnode + + # Not used in Python 3.8+. + def visit_nameconstant(self, node, parent): + # in Python 3.4 we have NameConstant for True / False / None + return nodes.Const( + node.value, + getattr(node, "lineno", None), + getattr(node, "col_offset", None), + parent, + ) + + def visit_nonlocal(self, node, parent): + """visit a Nonlocal node and return a new instance of it""" + return nodes.Nonlocal( + node.names, + getattr(node, "lineno", None), + getattr(node, "col_offset", None), + parent, + ) + + def visit_constant(self, node, parent): + """visit a Constant node by returning a fresh instance of Const""" + return nodes.Const( + node.value, + getattr(node, "lineno", None), + getattr(node, "col_offset", None), + parent, + ) + + # Not used in Python 3.8+. + def visit_str(self, node, parent): + """visit a String/Bytes node by returning a fresh instance of Const""" + return nodes.Const( + node.s, + getattr(node, "lineno", None), + getattr(node, "col_offset", None), + parent, + ) + + visit_bytes = visit_str + + # Not used in Python 3.8+. + def visit_num(self, node, parent): + """visit a Num node by returning a fresh instance of Const""" + return nodes.Const( + node.n, + getattr(node, "lineno", None), + getattr(node, "col_offset", None), + parent, + ) + + def visit_pass(self, node, parent): + """visit a Pass node by returning a fresh instance of it""" + return nodes.Pass(node.lineno, node.col_offset, parent) + + def visit_print(self, node, parent): + """visit a Print node by returning a fresh instance of it""" + newnode = nodes.Print(node.nl, node.lineno, node.col_offset, parent) + newnode.postinit( + _visit_or_none(node, "dest", self, newnode), + [self.visit(child, newnode) for child in node.values], + ) + return newnode + + def visit_raise(self, node, parent): + """visit a Raise node by returning a fresh instance of it""" + newnode = nodes.Raise(node.lineno, node.col_offset, parent) + # no traceback; anyway it is not used in Pylint + newnode.postinit( + _visit_or_none(node, "exc", self, newnode), + _visit_or_none(node, "cause", self, newnode), + ) + return newnode + + def visit_return(self, node, parent): + """visit a Return node by returning a fresh instance of it""" + newnode = nodes.Return(node.lineno, node.col_offset, parent) + if node.value is not None: + newnode.postinit(self.visit(node.value, newnode)) + return newnode + + def visit_set(self, node, parent): + """visit a Set node by returning a fresh instance of it""" + newnode = nodes.Set(node.lineno, node.col_offset, parent) + newnode.postinit([self.visit(child, newnode) for child in node.elts]) + return newnode + + def visit_setcomp(self, node, parent): + """visit a SetComp node by returning a fresh instance of it""" + newnode = nodes.SetComp(node.lineno, node.col_offset, parent) + newnode.postinit( + self.visit(node.elt, newnode), + [self.visit(child, newnode) for child in node.generators], + ) + return newnode + + def visit_slice(self, node, parent): + """visit a Slice node by returning a fresh instance of it""" + newnode = nodes.Slice(parent=parent) + newnode.postinit( + _visit_or_none(node, "lower", self, newnode), + _visit_or_none(node, "upper", self, newnode), + _visit_or_none(node, "step", self, newnode), + ) + return newnode + + def visit_subscript(self, node, parent): + """visit a Subscript node by returning a fresh instance of it""" + context = self._get_context(node) + newnode = nodes.Subscript( + ctx=context, lineno=node.lineno, col_offset=node.col_offset, parent=parent + ) + newnode.postinit( + self.visit(node.value, newnode), self.visit(node.slice, newnode) + ) + return newnode + + def visit_starred(self, node, parent): + """visit a Starred node and return a new instance of it""" + context = self._get_context(node) + newnode = nodes.Starred( + ctx=context, lineno=node.lineno, col_offset=node.col_offset, parent=parent + ) + newnode.postinit(self.visit(node.value, newnode)) + return newnode + + def visit_tryexcept(self, node, parent): + """visit a TryExcept node by returning a fresh instance of it""" + newnode = nodes.TryExcept(node.lineno, node.col_offset, parent) + newnode.postinit( + [self.visit(child, newnode) for child in node.body], + [self.visit(child, newnode) for child in node.handlers], + [self.visit(child, newnode) for child in node.orelse], + ) + return newnode + + def visit_try(self, node, parent): + # python 3.3 introduce a new Try node replacing + # TryFinally/TryExcept nodes + if node.finalbody: + newnode = nodes.TryFinally(node.lineno, node.col_offset, parent) + if node.handlers: + body = [self.visit_tryexcept(node, newnode)] + else: + body = [self.visit(child, newnode) for child in node.body] + newnode.postinit(body, [self.visit(n, newnode) for n in node.finalbody]) + return newnode + if node.handlers: + return self.visit_tryexcept(node, parent) + return None + + def visit_tryfinally(self, node, parent): + """visit a TryFinally node by returning a fresh instance of it""" + newnode = nodes.TryFinally(node.lineno, node.col_offset, parent) + newnode.postinit( + [self.visit(child, newnode) for child in node.body], + [self.visit(n, newnode) for n in node.finalbody], + ) + return newnode + + def visit_tuple(self, node, parent): + """visit a Tuple node by returning a fresh instance of it""" + context = self._get_context(node) + newnode = nodes.Tuple( + ctx=context, lineno=node.lineno, col_offset=node.col_offset, parent=parent + ) + newnode.postinit([self.visit(child, newnode) for child in node.elts]) + return newnode + + def visit_unaryop(self, node, parent): + """visit a UnaryOp node by returning a fresh instance of it""" + newnode = nodes.UnaryOp( + self._parser_module.unary_op_classes[node.op.__class__], + node.lineno, + node.col_offset, + parent, + ) + newnode.postinit(self.visit(node.operand, newnode)) + return newnode + + def visit_while(self, node, parent): + """visit a While node by returning a fresh instance of it""" + newnode = nodes.While(node.lineno, node.col_offset, parent) + newnode.postinit( + self.visit(node.test, newnode), + [self.visit(child, newnode) for child in node.body], + [self.visit(child, newnode) for child in node.orelse], + ) + return newnode + + def _visit_with(self, cls, node, parent): + newnode = cls(node.lineno, node.col_offset, parent) + + def visit_child(child): + expr = self.visit(child.context_expr, newnode) + var = _visit_or_none(child, "optional_vars", self, newnode) + return expr, var + + type_annotation = self.check_type_comment(node, parent=newnode) + newnode.postinit( + items=[visit_child(child) for child in node.items], + body=[self.visit(child, newnode) for child in node.body], + type_annotation=type_annotation, + ) + return newnode + + def visit_with(self, node, parent): + return self._visit_with(nodes.With, node, parent) + + def visit_yield(self, node, parent): + """visit a Yield node by returning a fresh instance of it""" + newnode = nodes.Yield(node.lineno, node.col_offset, parent) + if node.value is not None: + newnode.postinit(self.visit(node.value, newnode)) + return newnode + + def visit_yieldfrom(self, node, parent): + newnode = nodes.YieldFrom(node.lineno, node.col_offset, parent) + if node.value is not None: + newnode.postinit(self.visit(node.value, newnode)) + return newnode diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/scoped_nodes.py b/Display/.venv/lib/python3.7/site-packages/astroid/scoped_nodes.py new file mode 100644 index 0000000..8561e74 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/scoped_nodes.py @@ -0,0 +1,2927 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2010 Daniel Harding +# Copyright (c) 2011, 2013-2015 Google, Inc. +# Copyright (c) 2013-2020 Claudiu Popa +# Copyright (c) 2013 Phil Schaf +# Copyright (c) 2014 Eevee (Alex Munroe) +# Copyright (c) 2015-2016 Florian Bruhin +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2015 Rene Zhang +# Copyright (c) 2015 Philip Lorenz +# Copyright (c) 2016-2017 Derek Gustafson +# Copyright (c) 2017-2018 Bryce Guinta +# Copyright (c) 2017-2018 Ashley Whetter +# Copyright (c) 2017 Łukasz Rogalski +# Copyright (c) 2017 David Euresti +# Copyright (c) 2018-2019 Nick Drozd +# Copyright (c) 2018 Ville Skyttä +# Copyright (c) 2018 Anthony Sottile +# Copyright (c) 2018 HoverHell +# Copyright (c) 2019 Hugo van Kemenade +# Copyright (c) 2019 Peter de Blanc + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + + +""" +This module contains the classes for "scoped" node, i.e. which are opening a +new local scope in the language definition : Module, ClassDef, FunctionDef (and +Lambda, GeneratorExp, DictComp and SetComp to some extent). +""" + +import builtins +import sys +import io +import itertools +from typing import Optional, List + +from astroid import bases +from astroid import context as contextmod +from astroid import exceptions +from astroid import decorators as decorators_mod +from astroid.interpreter import objectmodel +from astroid.interpreter import dunder_lookup +from astroid import manager +from astroid import mixins +from astroid import node_classes +from astroid import util + + +BUILTINS = builtins.__name__ +ITER_METHODS = ("__iter__", "__getitem__") +EXCEPTION_BASE_CLASSES = frozenset({"Exception", "BaseException"}) +objects = util.lazy_import("objects") +BUILTIN_DESCRIPTORS = frozenset( + {"classmethod", "staticmethod", "builtins.classmethod", "builtins.staticmethod"} +) + + +def _c3_merge(sequences, cls, context): + """Merges MROs in *sequences* to a single MRO using the C3 algorithm. + + Adapted from http://www.python.org/download/releases/2.3/mro/. + + """ + result = [] + while True: + sequences = [s for s in sequences if s] # purge empty sequences + if not sequences: + return result + for s1 in sequences: # find merge candidates among seq heads + candidate = s1[0] + for s2 in sequences: + if candidate in s2[1:]: + candidate = None + break # reject the current head, it appears later + else: + break + if not candidate: + # Show all the remaining bases, which were considered as + # candidates for the next mro sequence. + raise exceptions.InconsistentMroError( + message="Cannot create a consistent method resolution order " + "for MROs {mros} of class {cls!r}.", + mros=sequences, + cls=cls, + context=context, + ) + + result.append(candidate) + # remove the chosen candidate + for seq in sequences: + if seq[0] == candidate: + del seq[0] + return None + + +def clean_duplicates_mro(sequences, cls, context): + for sequence in sequences: + names = [ + (node.lineno, node.qname()) if node.name else None for node in sequence + ] + last_index = dict(map(reversed, enumerate(names))) + if names and names[0] is not None and last_index[names[0]] != 0: + raise exceptions.DuplicateBasesError( + message="Duplicates found in MROs {mros} for {cls!r}.", + mros=sequences, + cls=cls, + context=context, + ) + yield [ + node + for i, (node, name) in enumerate(zip(sequence, names)) + if name is None or last_index[name] == i + ] + + +def function_to_method(n, klass): + if isinstance(n, FunctionDef): + if n.type == "classmethod": + return bases.BoundMethod(n, klass) + if n.type == "property": + return n + if n.type != "staticmethod": + return bases.UnboundMethod(n) + return n + + +MANAGER = manager.AstroidManager() + + +def builtin_lookup(name): + """lookup a name into the builtin module + return the list of matching statements and the astroid for the builtin + module + """ + builtin_astroid = MANAGER.ast_from_module(builtins) + if name == "__dict__": + return builtin_astroid, () + try: + stmts = builtin_astroid.locals[name] + except KeyError: + stmts = () + return builtin_astroid, stmts + + +# TODO move this Mixin to mixins.py; problem: 'FunctionDef' in _scope_lookup +class LocalsDictNodeNG(node_classes.LookupMixIn, node_classes.NodeNG): + """ this class provides locals handling common to Module, FunctionDef + and ClassDef nodes, including a dict like interface for direct access + to locals information + """ + + # attributes below are set by the builder module or by raw factories + + locals = {} + """A map of the name of a local variable to the node defining the local. + + :type: dict(str, NodeNG) + """ + + def qname(self): + """Get the 'qualified' name of the node. + + For example: module.name, module.class.name ... + + :returns: The qualified name. + :rtype: str + """ + # pylint: disable=no-member; github.com/pycqa/astroid/issues/278 + if self.parent is None: + return self.name + return "%s.%s" % (self.parent.frame().qname(), self.name) + + def frame(self): + """The first parent frame node. + + A frame node is a :class:`Module`, :class:`FunctionDef`, + or :class:`ClassDef`. + + :returns: The first parent frame node. + :rtype: Module or FunctionDef or ClassDef + """ + return self + + def scope(self): + """The first parent node defining a new scope. + + :returns: The first parent scope node. + :rtype: Module or FunctionDef or ClassDef or Lambda or GenExpr + """ + return self + + def _scope_lookup(self, node, name, offset=0): + """XXX method for interfacing the scope lookup""" + try: + stmts = node._filter_stmts(self.locals[name], self, offset) + except KeyError: + stmts = () + if stmts: + return self, stmts + if self.parent: # i.e. not Module + # nested scope: if parent scope is a function, that's fine + # else jump to the module + pscope = self.parent.scope() + if not pscope.is_function: + pscope = pscope.root() + return pscope.scope_lookup(node, name) + return builtin_lookup(name) # Module + + def set_local(self, name, stmt): + """Define that the given name is declared in the given statement node. + + .. seealso:: :meth:`scope` + + :param name: The name that is being defined. + :type name: str + + :param stmt: The statement that defines the given name. + :type stmt: NodeNG + """ + # assert not stmt in self.locals.get(name, ()), (self, stmt) + self.locals.setdefault(name, []).append(stmt) + + __setitem__ = set_local + + def _append_node(self, child): + """append a child, linking it in the tree""" + # pylint: disable=no-member; depending by the class + # which uses the current class as a mixin or base class. + # It's rewritten in 2.0, so it makes no sense for now + # to spend development time on it. + self.body.append(child) + child.parent = self + + def add_local_node(self, child_node, name=None): + """Append a child that should alter the locals of this scope node. + + :param child_node: The child node that will alter locals. + :type child_node: NodeNG + + :param name: The name of the local that will be altered by + the given child node. + :type name: str or None + """ + if name != "__class__": + # add __class__ node as a child will cause infinite recursion later! + self._append_node(child_node) + self.set_local(name or child_node.name, child_node) + + def __getitem__(self, item): + """The first node the defines the given local. + + :param item: The name of the locally defined object. + :type item: str + + :raises KeyError: If the name is not defined. + """ + return self.locals[item][0] + + def __iter__(self): + """Iterate over the names of locals defined in this scoped node. + + :returns: The names of the defined locals. + :rtype: iterable(str) + """ + return iter(self.keys()) + + def keys(self): + """The names of locals defined in this scoped node. + + :returns: The names of the defined locals. + :rtype: list(str) + """ + return list(self.locals.keys()) + + def values(self): + """The nodes that define the locals in this scoped node. + + :returns: The nodes that define locals. + :rtype: list(NodeNG) + """ + return [self[key] for key in self.keys()] + + def items(self): + """Get the names of the locals and the node that defines the local. + + :returns: The names of locals and their associated node. + :rtype: list(tuple(str, NodeNG)) + """ + return list(zip(self.keys(), self.values())) + + def __contains__(self, name): + """Check if a local is defined in this scope. + + :param name: The name of the local to check for. + :type name: str + + :returns: True if this node has a local of the given name, + False otherwise. + :rtype: bool + """ + return name in self.locals + + +class Module(LocalsDictNodeNG): + """Class representing an :class:`ast.Module` node. + + >>> node = astroid.extract_node('import astroid') + >>> node + + >>> node.parent + + """ + + _astroid_fields = ("body",) + + fromlineno = 0 + """The first line that this node appears on in the source code. + + :type: int or None + """ + lineno = 0 + """The line that this node appears on in the source code. + + :type: int or None + """ + + # attributes below are set by the builder module or by raw factories + + file = None + """The path to the file that this ast has been extracted from. + + This will be ``None`` when the representation has been built from a + built-in module. + + :type: str or None + """ + file_bytes = None + """The string/bytes that this ast was built from. + + :type: str or bytes or None + """ + file_encoding = None + """The encoding of the source file. + + This is used to get unicode out of a source file. + Python 2 only. + + :type: str or None + """ + name = None + """The name of the module. + + :type: str or None + """ + pure_python = None + """Whether the ast was built from source. + + :type: bool or None + """ + package = None + """Whether the node represents a package or a module. + + :type: bool or None + """ + globals = None + """A map of the name of a global variable to the node defining the global. + + :type: dict(str, NodeNG) + """ + + # Future imports + future_imports = None + """The imports from ``__future__``. + + :type: set(str) or None + """ + special_attributes = objectmodel.ModuleModel() + """The names of special attributes that this module has. + + :type: objectmodel.ModuleModel + """ + + # names of module attributes available through the global scope + scope_attrs = {"__name__", "__doc__", "__file__", "__path__", "__package__"} + """The names of module attributes available through the global scope. + + :type: str(str) + """ + + _other_fields = ( + "name", + "doc", + "file", + "path", + "package", + "pure_python", + "future_imports", + ) + _other_other_fields = ("locals", "globals") + + def __init__( + self, + name, + doc, + file=None, + path: Optional[List[str]] = None, + package=None, + parent=None, + pure_python=True, + ): + """ + :param name: The name of the module. + :type name: str + + :param doc: The module docstring. + :type doc: str + + :param file: The path to the file that this ast has been extracted from. + :type file: str or None + + :param path: + :type path: Optional[List[str]] + + :param package: Whether the node represents a package or a module. + :type package: bool or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + + :param pure_python: Whether the ast was built from source. + :type pure_python: bool or None + """ + self.name = name + self.doc = doc + self.file = file + self.path = path + self.package = package + self.parent = parent + self.pure_python = pure_python + self.locals = self.globals = {} + """A map of the name of a local variable to the node defining the local. + + :type: dict(str, NodeNG) + """ + self.body = [] + """The contents of the module. + + :type: list(NodeNG) or None + """ + self.future_imports = set() + + # pylint: enable=redefined-builtin + + def postinit(self, body=None): + """Do some setup after initialisation. + + :param body: The contents of the module. + :type body: list(NodeNG) or None + """ + self.body = body + + def _get_stream(self): + if self.file_bytes is not None: + return io.BytesIO(self.file_bytes) + if self.file is not None: + stream = open(self.file, "rb") + return stream + return None + + def stream(self): + """Get a stream to the underlying file or bytes. + + :type: file or io.BytesIO or None + """ + return self._get_stream() + + def block_range(self, lineno): + """Get a range from where this node starts to where this node ends. + + :param lineno: Unused. + :type lineno: int + + :returns: The range of line numbers that this node belongs to. + :rtype: tuple(int, int) + """ + return self.fromlineno, self.tolineno + + def scope_lookup(self, node, name, offset=0): + """Lookup where the given variable is assigned. + + :param node: The node to look for assignments up to. + Any assignments after the given node are ignored. + :type node: NodeNG + + :param name: The name of the variable to find assignments for. + :type name: str + + :param offset: The line offset to filter statements up to. + :type offset: int + + :returns: This scope node and the list of assignments associated to the + given name according to the scope where it has been found (locals, + globals or builtin). + :rtype: tuple(str, list(NodeNG)) + """ + if name in self.scope_attrs and name not in self.locals: + try: + return self, self.getattr(name) + except exceptions.AttributeInferenceError: + return self, () + return self._scope_lookup(node, name, offset) + + def pytype(self): + """Get the name of the type that this node represents. + + :returns: The name of the type. + :rtype: str + """ + return "%s.module" % BUILTINS + + def display_type(self): + """A human readable type of this node. + + :returns: The type of this node. + :rtype: str + """ + return "Module" + + def getattr(self, name, context=None, ignore_locals=False): + if not name: + raise exceptions.AttributeInferenceError( + target=self, attribute=name, context=context + ) + + result = [] + name_in_locals = name in self.locals + + if name in self.special_attributes and not ignore_locals and not name_in_locals: + result = [self.special_attributes.lookup(name)] + elif not ignore_locals and name_in_locals: + result = self.locals[name] + elif self.package: + try: + result = [self.import_module(name, relative_only=True)] + except (exceptions.AstroidBuildingError, SyntaxError) as exc: + raise exceptions.AttributeInferenceError( + target=self, attribute=name, context=context + ) from exc + result = [n for n in result if not isinstance(n, node_classes.DelName)] + if result: + return result + raise exceptions.AttributeInferenceError( + target=self, attribute=name, context=context + ) + + def igetattr(self, name, context=None): + """Infer the possible values of the given variable. + + :param name: The name of the variable to infer. + :type name: str + + :returns: The inferred possible values. + :rtype: iterable(NodeNG) or None + """ + # set lookup name since this is necessary to infer on import nodes for + # instance + context = contextmod.copy_context(context) + context.lookupname = name + try: + return bases._infer_stmts(self.getattr(name, context), context, frame=self) + except exceptions.AttributeInferenceError as error: + raise exceptions.InferenceError( + error.message, target=self, attribute=name, context=context + ) from error + + def fully_defined(self): + """Check if this module has been build from a .py file. + + If so, the module contains a complete representation, + including the code. + + :returns: True if the module has been built from a .py file. + :rtype: bool + """ + return self.file is not None and self.file.endswith(".py") + + def statement(self): + """The first parent node, including self, marked as statement node. + + :returns: The first parent statement. + :rtype: NodeNG + """ + return self + + def previous_sibling(self): + """The previous sibling statement. + + :returns: The previous sibling statement node. + :rtype: NodeNG or None + """ + + def next_sibling(self): + """The next sibling statement node. + + :returns: The next sibling statement node. + :rtype: NodeNG or None + """ + + _absolute_import_activated = True + + def absolute_import_activated(self): + """Whether :pep:`328` absolute import behaviour has been enabled. + + :returns: True if :pep:`328` has been enabled, False otherwise. + :rtype: bool + """ + return self._absolute_import_activated + + def import_module(self, modname, relative_only=False, level=None): + """Get the ast for a given module as if imported from this module. + + :param modname: The name of the module to "import". + :type modname: str + + :param relative_only: Whether to only consider relative imports. + :type relative_only: bool + + :param level: The level of relative import. + :type level: int or None + + :returns: The imported module ast. + :rtype: NodeNG + """ + if relative_only and level is None: + level = 0 + absmodname = self.relative_to_absolute_name(modname, level) + + try: + return MANAGER.ast_from_module_name(absmodname) + except exceptions.AstroidBuildingError: + # we only want to import a sub module or package of this module, + # skip here + if relative_only: + raise + return MANAGER.ast_from_module_name(modname) + + def relative_to_absolute_name(self, modname, level): + """Get the absolute module name for a relative import. + + The relative import can be implicit or explicit. + + :param modname: The module name to convert. + :type modname: str + + :param level: The level of relative import. + :type level: int + + :returns: The absolute module name. + :rtype: str + + :raises TooManyLevelsError: When the relative import refers to a + module too far above this one. + """ + # XXX this returns non sens when called on an absolute import + # like 'pylint.checkers.astroid.utils' + # XXX doesn't return absolute name if self.name isn't absolute name + if self.absolute_import_activated() and level is None: + return modname + if level: + if self.package: + level = level - 1 + if level and self.name.count(".") < level: + raise exceptions.TooManyLevelsError(level=level, name=self.name) + + package_name = self.name.rsplit(".", level)[0] + elif self.package: + package_name = self.name + else: + package_name = self.name.rsplit(".", 1)[0] + + if package_name: + if not modname: + return package_name + return "%s.%s" % (package_name, modname) + return modname + + def wildcard_import_names(self): + """The list of imported names when this module is 'wildcard imported'. + + It doesn't include the '__builtins__' name which is added by the + current CPython implementation of wildcard imports. + + :returns: The list of imported names. + :rtype: list(str) + """ + # We separate the different steps of lookup in try/excepts + # to avoid catching too many Exceptions + default = [name for name in self.keys() if not name.startswith("_")] + try: + all_values = self["__all__"] + except KeyError: + return default + + try: + explicit = next(all_values.assigned_stmts()) + except exceptions.InferenceError: + return default + except AttributeError: + # not an assignment node + # XXX infer? + return default + + # Try our best to detect the exported name. + inferred = [] + try: + explicit = next(explicit.infer()) + except exceptions.InferenceError: + return default + if not isinstance(explicit, (node_classes.Tuple, node_classes.List)): + return default + + str_const = lambda node: ( + isinstance(node, node_classes.Const) and isinstance(node.value, str) + ) + for node in explicit.elts: + if str_const(node): + inferred.append(node.value) + else: + try: + inferred_node = next(node.infer()) + except exceptions.InferenceError: + continue + if str_const(inferred_node): + inferred.append(inferred_node.value) + return inferred + + def public_names(self): + """The list of the names that are publicly available in this module. + + :returns: The list of publc names. + :rtype: list(str) + """ + return [name for name in self.keys() if not name.startswith("_")] + + def bool_value(self, context=None): + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + For a :class:`Module` this is always ``True``. + :rtype: bool + """ + return True + + def get_children(self): + yield from self.body + + +class ComprehensionScope(LocalsDictNodeNG): + """Scoping for different types of comprehensions.""" + + def frame(self): + """The first parent frame node. + + A frame node is a :class:`Module`, :class:`FunctionDef`, + or :class:`ClassDef`. + + :returns: The first parent frame node. + :rtype: Module or FunctionDef or ClassDef + """ + return self.parent.frame() + + scope_lookup = LocalsDictNodeNG._scope_lookup + + +class GeneratorExp(ComprehensionScope): + """Class representing an :class:`ast.GeneratorExp` node. + + >>> node = astroid.extract_node('(thing for thing in things if thing)') + >>> node + + """ + + _astroid_fields = ("elt", "generators") + _other_other_fields = ("locals",) + elt = None + """The element that forms the output of the expression. + + :type: NodeNG or None + """ + generators = None + """The generators that are looped through. + + :type: list(Comprehension) or None + """ + + def __init__(self, lineno=None, col_offset=None, parent=None): + """ + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.locals = {} + """A map of the name of a local variable to the node defining the local. + + :type: dict(str, NodeNG) + """ + + super().__init__(lineno, col_offset, parent) + + def postinit(self, elt=None, generators=None): + """Do some setup after initialisation. + + :param elt: The element that forms the output of the expression. + :type elt: NodeNG or None + + :param generators: The generators that are looped through. + :type generators: list(Comprehension) or None + """ + self.elt = elt + if generators is None: + self.generators = [] + else: + self.generators = generators + + def bool_value(self, context=None): + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + For a :class:`GeneratorExp` this is always ``True``. + :rtype: bool + """ + return True + + def get_children(self): + yield self.elt + + yield from self.generators + + +class DictComp(ComprehensionScope): + """Class representing an :class:`ast.DictComp` node. + + >>> node = astroid.extract_node('{k:v for k, v in things if k > v}') + >>> node + + """ + + _astroid_fields = ("key", "value", "generators") + _other_other_fields = ("locals",) + key = None + """What produces the keys. + + :type: NodeNG or None + """ + value = None + """What produces the values. + + :type: NodeNG or None + """ + generators = None + """The generators that are looped through. + + :type: list(Comprehension) or None + """ + + def __init__(self, lineno=None, col_offset=None, parent=None): + """ + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.locals = {} + """A map of the name of a local variable to the node defining the local. + + :type: dict(str, NodeNG) + """ + + super().__init__(lineno, col_offset, parent) + + def postinit(self, key=None, value=None, generators=None): + """Do some setup after initialisation. + + :param key: What produces the keys. + :type key: NodeNG or None + + :param value: What produces the values. + :type value: NodeNG or None + + :param generators: The generators that are looped through. + :type generators: list(Comprehension) or None + """ + self.key = key + self.value = value + if generators is None: + self.generators = [] + else: + self.generators = generators + + def bool_value(self, context=None): + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + For a :class:`DictComp` this is always :class:`Uninferable`. + :rtype: Uninferable + """ + return util.Uninferable + + def get_children(self): + yield self.key + yield self.value + + yield from self.generators + + +class SetComp(ComprehensionScope): + """Class representing an :class:`ast.SetComp` node. + + >>> node = astroid.extract_node('{thing for thing in things if thing}') + >>> node + + """ + + _astroid_fields = ("elt", "generators") + _other_other_fields = ("locals",) + elt = None + """The element that forms the output of the expression. + + :type: NodeNG or None + """ + generators = None + """The generators that are looped through. + + :type: list(Comprehension) or None + """ + + def __init__(self, lineno=None, col_offset=None, parent=None): + """ + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.locals = {} + """A map of the name of a local variable to the node defining the local. + + :type: dict(str, NodeNG) + """ + + super().__init__(lineno, col_offset, parent) + + def postinit(self, elt=None, generators=None): + """Do some setup after initialisation. + + :param elt: The element that forms the output of the expression. + :type elt: NodeNG or None + + :param generators: The generators that are looped through. + :type generators: list(Comprehension) or None + """ + self.elt = elt + if generators is None: + self.generators = [] + else: + self.generators = generators + + def bool_value(self, context=None): + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + For a :class:`SetComp` this is always :class:`Uninferable`. + :rtype: Uninferable + """ + return util.Uninferable + + def get_children(self): + yield self.elt + + yield from self.generators + + +class _ListComp(node_classes.NodeNG): + """Class representing an :class:`ast.ListComp` node. + + >>> node = astroid.extract_node('[thing for thing in things if thing]') + >>> node + + """ + + _astroid_fields = ("elt", "generators") + elt = None + """The element that forms the output of the expression. + + :type: NodeNG or None + """ + generators = None + """The generators that are looped through. + + :type: list(Comprehension) or None + """ + + def postinit(self, elt=None, generators=None): + """Do some setup after initialisation. + + :param elt: The element that forms the output of the expression. + :type elt: NodeNG or None + + :param generators: The generators that are looped through. + :type generators: list(Comprehension) or None + """ + self.elt = elt + self.generators = generators + + def bool_value(self, context=None): + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + For a :class:`ListComp` this is always :class:`Uninferable`. + :rtype: Uninferable + """ + return util.Uninferable + + def get_children(self): + yield self.elt + + yield from self.generators + + +class ListComp(_ListComp, ComprehensionScope): + """Class representing an :class:`ast.ListComp` node. + + >>> node = astroid.extract_node('[thing for thing in things if thing]') + >>> node + + """ + + _other_other_fields = ("locals",) + + def __init__(self, lineno=None, col_offset=None, parent=None): + self.locals = {} + """A map of the name of a local variable to the node defining it. + + :type: dict(str, NodeNG) + """ + + super().__init__(lineno, col_offset, parent) + + +def _infer_decorator_callchain(node): + """Detect decorator call chaining and see if the end result is a + static or a classmethod. + """ + if not isinstance(node, FunctionDef): + return None + if not node.parent: + return None + try: + result = next(node.infer_call_result(node.parent)) + except exceptions.InferenceError: + return None + if isinstance(result, bases.Instance): + result = result._proxied + if isinstance(result, ClassDef): + if result.is_subtype_of("%s.classmethod" % BUILTINS): + return "classmethod" + if result.is_subtype_of("%s.staticmethod" % BUILTINS): + return "staticmethod" + if isinstance(result, FunctionDef): + if not result.decorators: + return None + # Determine if this function is decorated with one of the builtin descriptors we want. + for decorator in result.decorators.nodes: + if isinstance(decorator, node_classes.Name): + if decorator.name in BUILTIN_DESCRIPTORS: + return decorator.name + if ( + isinstance(decorator, node_classes.Attribute) + and isinstance(decorator.expr, node_classes.Name) + and decorator.expr.name == BUILTINS + and decorator.attrname in BUILTIN_DESCRIPTORS + ): + return decorator.attrname + return None + + +class Lambda(mixins.FilterStmtsMixin, LocalsDictNodeNG): + """Class representing an :class:`ast.Lambda` node. + + >>> node = astroid.extract_node('lambda arg: arg + 1') + >>> node + l.1 at 0x7f23b2e41518> + """ + + _astroid_fields = ("args", "body") + _other_other_fields = ("locals",) + name = "" + is_lambda = True + + def implicit_parameters(self): + return 0 + + # function's type, 'function' | 'method' | 'staticmethod' | 'classmethod' + @property + def type(self): + """Whether this is a method or function. + + :returns: 'method' if this is a method, 'function' otherwise. + :rtype: str + """ + # pylint: disable=no-member + if self.args.arguments and self.args.arguments[0].name == "self": + if isinstance(self.parent.scope(), ClassDef): + return "method" + return "function" + + def __init__(self, lineno=None, col_offset=None, parent=None): + """ + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.locals = {} + """A map of the name of a local variable to the node defining it. + + :type: dict(str, NodeNG) + """ + + self.args = [] + """The arguments that the function takes. + + :type: Arguments or list + """ + + self.body = [] + """The contents of the function body. + + :type: list(NodeNG) + """ + + super().__init__(lineno, col_offset, parent) + + def postinit(self, args, body): + """Do some setup after initialisation. + + :param args: The arguments that the function takes. + :type args: Arguments + + :param body: The contents of the function body. + :type body: list(NodeNG) + """ + self.args = args + self.body = body + + def pytype(self): + """Get the name of the type that this node represents. + + :returns: The name of the type. + :rtype: str + """ + if "method" in self.type: + return "%s.instancemethod" % BUILTINS + return "%s.function" % BUILTINS + + def display_type(self): + """A human readable type of this node. + + :returns: The type of this node. + :rtype: str + """ + if "method" in self.type: + return "Method" + return "Function" + + def callable(self): + """Whether this node defines something that is callable. + + :returns: True if this defines something that is callable, + False otherwise. + For a :class:`Lambda` this is always ``True``. + :rtype: bool + """ + return True + + def argnames(self): + """Get the names of each of the arguments. + + :returns: The names of the arguments. + :rtype: list(str) + """ + # pylint: disable=no-member; github.com/pycqa/astroid/issues/291 + # args is in fact redefined later on by postinit. Can't be changed + # to None due to a strong interaction between Lambda and FunctionDef. + + if self.args.arguments: # maybe None with builtin functions + names = _rec_get_names(self.args.arguments) + else: + names = [] + if self.args.vararg: + names.append(self.args.vararg) + if self.args.kwarg: + names.append(self.args.kwarg) + return names + + def infer_call_result(self, caller, context=None): + """Infer what the function returns when called. + + :param caller: Unused + :type caller: object + """ + # pylint: disable=no-member; github.com/pycqa/astroid/issues/291 + # args is in fact redefined later on by postinit. Can't be changed + # to None due to a strong interaction between Lambda and FunctionDef. + return self.body.infer(context) + + def scope_lookup(self, node, name, offset=0): + """Lookup where the given names is assigned. + + :param node: The node to look for assignments up to. + Any assignments after the given node are ignored. + :type node: NodeNG + + :param name: The name to find assignments for. + :type name: str + + :param offset: The line offset to filter statements up to. + :type offset: int + + :returns: This scope node and the list of assignments associated to the + given name according to the scope where it has been found (locals, + globals or builtin). + :rtype: tuple(str, list(NodeNG)) + """ + # pylint: disable=no-member; github.com/pycqa/astroid/issues/291 + # args is in fact redefined later on by postinit. Can't be changed + # to None due to a strong interaction between Lambda and FunctionDef. + + if node in self.args.defaults or node in self.args.kw_defaults: + frame = self.parent.frame() + # line offset to avoid that def func(f=func) resolve the default + # value to the defined function + offset = -1 + else: + # check this is not used in function decorators + frame = self + return frame._scope_lookup(node, name, offset) + + def bool_value(self, context=None): + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + For a :class:`Lambda` this is always ``True``. + :rtype: bool + """ + return True + + def get_children(self): + yield self.args + yield self.body + + +class FunctionDef(mixins.MultiLineBlockMixin, node_classes.Statement, Lambda): + """Class representing an :class:`ast.FunctionDef`. + + >>> node = astroid.extract_node(''' + ... def my_func(arg): + ... return arg + 1 + ... ''') + >>> node + + """ + + _astroid_fields = ("decorators", "args", "returns", "body") + _multi_line_block_fields = ("body",) + returns = None + decorators = None + """The decorators that are applied to this method or function. + + :type: Decorators or None + """ + special_attributes = objectmodel.FunctionModel() + """The names of special attributes that this function has. + + :type: objectmodel.FunctionModel + """ + is_function = True + """Whether this node indicates a function. + + For a :class:`FunctionDef` this is always ``True``. + + :type: bool + """ + type_annotation = None + """If present, this will contain the type annotation passed by a type comment + + :type: NodeNG or None + """ + type_comment_args = None + """ + If present, this will contain the type annotation for arguments + passed by a type comment + """ + type_comment_returns = None + """If present, this will contain the return type annotation, passed by a type comment""" + # attributes below are set by the builder module or by raw factories + _other_fields = ("name", "doc") + _other_other_fields = ( + "locals", + "_type", + "type_comment_returns", + "type_comment_args", + ) + _type = None + + def __init__(self, name=None, doc=None, lineno=None, col_offset=None, parent=None): + """ + :param name: The name of the function. + :type name: str or None + + :param doc: The function's docstring. + :type doc: str or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.name = name + """The name of the function. + + :type name: str or None + """ + + self.doc = doc + """The function's docstring. + + :type doc: str or None + """ + + self.instance_attrs = {} + super().__init__(lineno, col_offset, parent) + if parent: + frame = parent.frame() + frame.set_local(name, self) + + # pylint: disable=arguments-differ; different than Lambdas + def postinit( + self, + args, + body, + decorators=None, + returns=None, + type_comment_returns=None, + type_comment_args=None, + ): + """Do some setup after initialisation. + + :param args: The arguments that the function takes. + :type args: Arguments or list + + :param body: The contents of the function body. + :type body: list(NodeNG) + + :param decorators: The decorators that are applied to this + method or function. + :type decorators: Decorators or None + :params type_comment_returns: + The return type annotation passed via a type comment. + :params type_comment_args: + The args type annotation passed via a type comment. + """ + self.args = args + self.body = body + self.decorators = decorators + self.returns = returns + self.type_comment_returns = type_comment_returns + self.type_comment_args = type_comment_args + + @decorators_mod.cachedproperty + def extra_decorators(self): + """The extra decorators that this function can have. + + Additional decorators are considered when they are used as + assignments, as in ``method = staticmethod(method)``. + The property will return all the callables that are used for + decoration. + + :type: list(NodeNG) + """ + frame = self.parent.frame() + if not isinstance(frame, ClassDef): + return [] + + decorators = [] + for assign in frame._get_assign_nodes(): + if isinstance(assign.value, node_classes.Call) and isinstance( + assign.value.func, node_classes.Name + ): + for assign_node in assign.targets: + if not isinstance(assign_node, node_classes.AssignName): + # Support only `name = callable(name)` + continue + + if assign_node.name != self.name: + # Interested only in the assignment nodes that + # decorates the current method. + continue + try: + meth = frame[self.name] + except KeyError: + continue + else: + # Must be a function and in the same frame as the + # original method. + if ( + isinstance(meth, FunctionDef) + and assign_node.frame() == frame + ): + decorators.append(assign.value) + return decorators + + @decorators_mod.cachedproperty + def type( + self + ): # pylint: disable=invalid-overridden-method,too-many-return-statements + """The function type for this node. + + Possible values are: method, function, staticmethod, classmethod. + + :type: str + """ + for decorator in self.extra_decorators: + if decorator.func.name in BUILTIN_DESCRIPTORS: + return decorator.func.name + + frame = self.parent.frame() + type_name = "function" + if isinstance(frame, ClassDef): + if self.name == "__new__": + return "classmethod" + if sys.version_info >= (3, 6) and self.name == "__init_subclass__": + return "classmethod" + + type_name = "method" + + if not self.decorators: + return type_name + + for node in self.decorators.nodes: + if isinstance(node, node_classes.Name): + if node.name in BUILTIN_DESCRIPTORS: + return node.name + if ( + isinstance(node, node_classes.Attribute) + and isinstance(node.expr, node_classes.Name) + and node.expr.name == BUILTINS + and node.attrname in BUILTIN_DESCRIPTORS + ): + return node.attrname + + if isinstance(node, node_classes.Call): + # Handle the following case: + # @some_decorator(arg1, arg2) + # def func(...) + # + try: + current = next(node.func.infer()) + except exceptions.InferenceError: + continue + _type = _infer_decorator_callchain(current) + if _type is not None: + return _type + + try: + for inferred in node.infer(): + # Check to see if this returns a static or a class method. + _type = _infer_decorator_callchain(inferred) + if _type is not None: + return _type + + if not isinstance(inferred, ClassDef): + continue + for ancestor in inferred.ancestors(): + if not isinstance(ancestor, ClassDef): + continue + if ancestor.is_subtype_of("%s.classmethod" % BUILTINS): + return "classmethod" + if ancestor.is_subtype_of("%s.staticmethod" % BUILTINS): + return "staticmethod" + except exceptions.InferenceError: + pass + return type_name + + @decorators_mod.cachedproperty + def fromlineno(self): + """The first line that this node appears on in the source code. + + :type: int or None + """ + # lineno is the line number of the first decorator, we want the def + # statement lineno + lineno = self.lineno + if self.decorators is not None: + lineno += sum( + node.tolineno - node.lineno + 1 for node in self.decorators.nodes + ) + + return lineno + + @decorators_mod.cachedproperty + def blockstart_tolineno(self): + """The line on which the beginning of this block ends. + + :type: int + """ + return self.args.tolineno + + def block_range(self, lineno): + """Get a range from the given line number to where this node ends. + + :param lineno: Unused. + :type lineno: int + + :returns: The range of line numbers that this node belongs to, + :rtype: tuple(int, int) + """ + return self.fromlineno, self.tolineno + + def getattr(self, name, context=None): + """this method doesn't look in the instance_attrs dictionary since it's + done by an Instance proxy at inference time. + """ + if not name: + raise exceptions.AttributeInferenceError( + target=self, attribute=name, context=context + ) + + found_attrs = [] + if name in self.instance_attrs: + found_attrs = self.instance_attrs[name] + if name in self.special_attributes: + found_attrs.append(self.special_attributes.lookup(name)) + if found_attrs: + return found_attrs + raise exceptions.AttributeInferenceError(target=self, attribute=name) + + def igetattr(self, name, context=None): + """Inferred getattr, which returns an iterator of inferred statements.""" + try: + return bases._infer_stmts(self.getattr(name, context), context, frame=self) + except exceptions.AttributeInferenceError as error: + raise exceptions.InferenceError( + error.message, target=self, attribute=name, context=context + ) from error + + def is_method(self): + """Check if this function node represents a method. + + :returns: True if this is a method, False otherwise. + :rtype: bool + """ + # check we are defined in a ClassDef, because this is usually expected + # (e.g. pylint...) when is_method() return True + return self.type != "function" and isinstance(self.parent.frame(), ClassDef) + + @decorators_mod.cached + def decoratornames(self, context=None): + """Get the qualified names of each of the decorators on this function. + + :param context: + An inference context that can be passed to inference functions + :returns: The names of the decorators. + :rtype: set(str) + """ + result = set() + decoratornodes = [] + if self.decorators is not None: + decoratornodes += self.decorators.nodes + decoratornodes += self.extra_decorators + for decnode in decoratornodes: + try: + for infnode in decnode.infer(context=context): + result.add(infnode.qname()) + except exceptions.InferenceError: + continue + return result + + def is_bound(self): + """Check if the function is bound to an instance or class. + + :returns: True if the function is bound to an instance or class, + False otherwise. + :rtype: bool + """ + return self.type == "classmethod" + + def is_abstract(self, pass_is_abstract=True): + """Check if the method is abstract. + + A method is considered abstract if any of the following is true: + * The only statement is 'raise NotImplementedError' + * The only statement is 'pass' and pass_is_abstract is True + * The method is annotated with abc.astractproperty/abc.abstractmethod + + :returns: True if the method is abstract, False otherwise. + :rtype: bool + """ + if self.decorators: + for node in self.decorators.nodes: + try: + inferred = next(node.infer()) + except exceptions.InferenceError: + continue + if inferred and inferred.qname() in ( + "abc.abstractproperty", + "abc.abstractmethod", + ): + return True + + for child_node in self.body: + if isinstance(child_node, node_classes.Raise): + if child_node.raises_not_implemented(): + return True + return pass_is_abstract and isinstance(child_node, node_classes.Pass) + # empty function is the same as function with a single "pass" statement + if pass_is_abstract: + return True + + def is_generator(self): + """Check if this is a generator function. + + :returns: True is this is a generator function, False otherwise. + :rtype: bool + """ + return bool(next(self._get_yield_nodes_skip_lambdas(), False)) + + def infer_call_result(self, caller=None, context=None): + """Infer what the function returns when called. + + :returns: What the function returns. + :rtype: iterable(NodeNG or Uninferable) or None + """ + if self.is_generator(): + if isinstance(self, AsyncFunctionDef): + generator_cls = bases.AsyncGenerator + else: + generator_cls = bases.Generator + result = generator_cls(self) + yield result + return + # This is really a gigantic hack to work around metaclass generators + # that return transient class-generating functions. Pylint's AST structure + # cannot handle a base class object that is only used for calling __new__, + # but does not contribute to the inheritance structure itself. We inject + # a fake class into the hierarchy here for several well-known metaclass + # generators, and filter it out later. + if ( + self.name == "with_metaclass" + and len(self.args.args) == 1 + and self.args.vararg is not None + ): + metaclass = next(caller.args[0].infer(context)) + if isinstance(metaclass, ClassDef): + class_bases = [next(arg.infer(context)) for arg in caller.args[1:]] + new_class = ClassDef(name="temporary_class") + new_class.hide = True + new_class.parent = self + new_class.postinit( + bases=[base for base in class_bases if base != util.Uninferable], + body=[], + decorators=[], + metaclass=metaclass, + ) + yield new_class + return + returns = self._get_return_nodes_skip_functions() + + first_return = next(returns, None) + if not first_return: + if self.body and isinstance(self.body[-1], node_classes.Assert): + yield node_classes.Const(None) + return + + raise exceptions.InferenceError( + "The function does not have any return statements" + ) + + for returnnode in itertools.chain((first_return,), returns): + if returnnode.value is None: + yield node_classes.Const(None) + else: + try: + yield from returnnode.value.infer(context) + except exceptions.InferenceError: + yield util.Uninferable + + def bool_value(self, context=None): + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + For a :class:`FunctionDef` this is always ``True``. + :rtype: bool + """ + return True + + def get_children(self): + if self.decorators is not None: + yield self.decorators + + yield self.args + + if self.returns is not None: + yield self.returns + + yield from self.body + + def scope_lookup(self, node, name, offset=0): + """Lookup where the given name is assigned.""" + if name == "__class__": + # __class__ is an implicit closure reference created by the compiler + # if any methods in a class body refer to either __class__ or super. + # In our case, we want to be able to look it up in the current scope + # when `__class__` is being used. + frame = self.parent.frame() + if isinstance(frame, ClassDef): + return self, [frame] + return super().scope_lookup(node, name, offset) + + +class AsyncFunctionDef(FunctionDef): + """Class representing an :class:`ast.FunctionDef` node. + + A :class:`AsyncFunctionDef` is an asynchronous function + created with the `async` keyword. + + >>> node = astroid.extract_node(''' + async def func(things): + async for thing in things: + print(thing) + ''') + >>> node + + >>> node.body[0] + + """ + + +def _rec_get_names(args, names=None): + """return a list of all argument names""" + if names is None: + names = [] + for arg in args: + if isinstance(arg, node_classes.Tuple): + _rec_get_names(arg.elts, names) + else: + names.append(arg.name) + return names + + +def _is_metaclass(klass, seen=None): + """ Return if the given class can be + used as a metaclass. + """ + if klass.name == "type": + return True + if seen is None: + seen = set() + for base in klass.bases: + try: + for baseobj in base.infer(): + baseobj_name = baseobj.qname() + if baseobj_name in seen: + continue + + seen.add(baseobj_name) + if isinstance(baseobj, bases.Instance): + # not abstract + return False + if baseobj is util.Uninferable: + continue + if baseobj is klass: + continue + if not isinstance(baseobj, ClassDef): + continue + if baseobj._type == "metaclass": + return True + if _is_metaclass(baseobj, seen): + return True + except exceptions.InferenceError: + continue + return False + + +def _class_type(klass, ancestors=None): + """return a ClassDef node type to differ metaclass and exception + from 'regular' classes + """ + # XXX we have to store ancestors in case we have an ancestor loop + if klass._type is not None: + return klass._type + if _is_metaclass(klass): + klass._type = "metaclass" + elif klass.name.endswith("Exception"): + klass._type = "exception" + else: + if ancestors is None: + ancestors = set() + klass_name = klass.qname() + if klass_name in ancestors: + # XXX we are in loop ancestors, and have found no type + klass._type = "class" + return "class" + ancestors.add(klass_name) + for base in klass.ancestors(recurs=False): + name = _class_type(base, ancestors) + if name != "class": + if name == "metaclass" and not _is_metaclass(klass): + # don't propagate it if the current class + # can't be a metaclass + continue + klass._type = base.type + break + if klass._type is None: + klass._type = "class" + return klass._type + + +def get_wrapping_class(node): + """Get the class that wraps the given node. + + We consider that a class wraps a node if the class + is a parent for the said node. + + :returns: The class that wraps the given node + :rtype: ClassDef or None + """ + + klass = node.frame() + while klass is not None and not isinstance(klass, ClassDef): + if klass.parent is None: + klass = None + else: + klass = klass.parent.frame() + return klass + + +class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG, node_classes.Statement): + """Class representing an :class:`ast.ClassDef` node. + + >>> node = astroid.extract_node(''' + class Thing: + def my_meth(self, arg): + return arg + self.offset + ''') + >>> node + + """ + + # some of the attributes below are set by the builder module or + # by a raw factories + + # a dictionary of class instances attributes + _astroid_fields = ("decorators", "bases", "body") # name + + decorators = None + """The decorators that are applied to this class. + + :type: Decorators or None + """ + special_attributes = objectmodel.ClassModel() + """The names of special attributes that this class has. + + :type: objectmodel.ClassModel + """ + + _type = None + _metaclass_hack = False + hide = False + type = property( + _class_type, + doc=( + "The class type for this node.\n\n" + "Possible values are: class, metaclass, exception.\n\n" + ":type: str" + ), + ) + _other_fields = ("name", "doc") + _other_other_fields = ("locals", "_newstyle") + _newstyle = None + + def __init__(self, name=None, doc=None, lineno=None, col_offset=None, parent=None): + """ + :param name: The name of the class. + :type name: str or None + + :param doc: The function's docstring. + :type doc: str or None + + :param lineno: The line that this node appears on in the source code. + :type lineno: int or None + + :param col_offset: The column that this node appears on in the + source code. + :type col_offset: int or None + + :param parent: The parent node in the syntax tree. + :type parent: NodeNG or None + """ + self.instance_attrs = {} + self.locals = {} + """A map of the name of a local variable to the node defining it. + + :type: dict(str, NodeNG) + """ + + self.keywords = [] + """The keywords given to the class definition. + + This is usually for :pep:`3115` style metaclass declaration. + + :type: list(Keyword) or None + """ + + self.bases = [] + """What the class inherits from. + + :type: list(NodeNG) + """ + + self.body = [] + """The contents of the class body. + + :type: list(NodeNG) + """ + + self.name = name + """The name of the class. + + :type name: str or None + """ + + self.doc = doc + """The class' docstring. + + :type doc: str or None + """ + + super().__init__(lineno, col_offset, parent) + if parent is not None: + parent.frame().set_local(name, self) + + for local_name, node in self.implicit_locals(): + self.add_local_node(node, local_name) + + def implicit_parameters(self): + return 1 + + def implicit_locals(self): + """Get implicitly defined class definition locals. + + :returns: the the name and Const pair for each local + :rtype: tuple(tuple(str, node_classes.Const), ...) + """ + locals_ = (("__module__", self.special_attributes.attr___module__),) + # __qualname__ is defined in PEP3155 + locals_ += (("__qualname__", self.special_attributes.attr___qualname__),) + return locals_ + + # pylint: disable=redefined-outer-name + def postinit( + self, bases, body, decorators, newstyle=None, metaclass=None, keywords=None + ): + """Do some setup after initialisation. + + :param bases: What the class inherits from. + :type bases: list(NodeNG) + + :param body: The contents of the class body. + :type body: list(NodeNG) + + :param decorators: The decorators that are applied to this class. + :type decorators: Decorators or None + + :param newstyle: Whether this is a new style class or not. + :type newstyle: bool or None + + :param metaclass: The metaclass of this class. + :type metaclass: NodeNG or None + + :param keywords: The keywords given to the class definition. + :type keywords: list(Keyword) or None + """ + self.keywords = keywords + self.bases = bases + self.body = body + self.decorators = decorators + if newstyle is not None: + self._newstyle = newstyle + if metaclass is not None: + self._metaclass = metaclass + + def _newstyle_impl(self, context=None): + if context is None: + context = contextmod.InferenceContext() + if self._newstyle is not None: + return self._newstyle + for base in self.ancestors(recurs=False, context=context): + if base._newstyle_impl(context): + self._newstyle = True + break + klass = self.declared_metaclass() + # could be any callable, we'd need to infer the result of klass(name, + # bases, dict). punt if it's not a class node. + if klass is not None and isinstance(klass, ClassDef): + self._newstyle = klass._newstyle_impl(context) + if self._newstyle is None: + self._newstyle = False + return self._newstyle + + _newstyle = None + newstyle = property( + _newstyle_impl, + doc=("Whether this is a new style class or not\n\n" ":type: bool or None"), + ) + + @decorators_mod.cachedproperty + def blockstart_tolineno(self): + """The line on which the beginning of this block ends. + + :type: int + """ + if self.bases: + return self.bases[-1].tolineno + + return self.fromlineno + + def block_range(self, lineno): + """Get a range from the given line number to where this node ends. + + :param lineno: Unused. + :type lineno: int + + :returns: The range of line numbers that this node belongs to, + :rtype: tuple(int, int) + """ + return self.fromlineno, self.tolineno + + def pytype(self): + """Get the name of the type that this node represents. + + :returns: The name of the type. + :rtype: str + """ + if self.newstyle: + return "%s.type" % BUILTINS + return "%s.classobj" % BUILTINS + + def display_type(self): + """A human readable type of this node. + + :returns: The type of this node. + :rtype: str + """ + return "Class" + + def callable(self): + """Whether this node defines something that is callable. + + :returns: True if this defines something that is callable, + False otherwise. + For a :class:`ClassDef` this is always ``True``. + :rtype: bool + """ + return True + + def is_subtype_of(self, type_name, context=None): + """Whether this class is a subtype of the given type. + + :param type_name: The name of the type of check against. + :type type_name: str + + :returns: True if this class is a subtype of the given type, + False otherwise. + :rtype: bool + """ + if self.qname() == type_name: + return True + for anc in self.ancestors(context=context): + if anc.qname() == type_name: + return True + return False + + def _infer_type_call(self, caller, context): + name_node = next(caller.args[0].infer(context)) + if isinstance(name_node, node_classes.Const) and isinstance( + name_node.value, str + ): + name = name_node.value + else: + return util.Uninferable + + result = ClassDef(name, None) + + # Get the bases of the class. + class_bases = next(caller.args[1].infer(context)) + if isinstance(class_bases, (node_classes.Tuple, node_classes.List)): + bases = [] + for base in class_bases.itered(): + inferred = next(base.infer(context=context)) + if inferred: + bases.append( + node_classes.EvaluatedObject(original=base, value=inferred) + ) + result.bases = bases + else: + # There is currently no AST node that can represent an 'unknown' + # node (Uninferable is not an AST node), therefore we simply return Uninferable here + # although we know at least the name of the class. + return util.Uninferable + + # Get the members of the class + try: + members = next(caller.args[2].infer(context)) + except exceptions.InferenceError: + members = None + + if members and isinstance(members, node_classes.Dict): + for attr, value in members.items: + if isinstance(attr, node_classes.Const) and isinstance(attr.value, str): + result.locals[attr.value] = [value] + + result.parent = caller.parent + return result + + def infer_call_result(self, caller, context=None): + """infer what a class is returning when called""" + if ( + self.is_subtype_of("%s.type" % (BUILTINS,), context) + and len(caller.args) == 3 + ): + result = self._infer_type_call(caller, context) + yield result + return + + dunder_call = None + try: + metaclass = self.metaclass(context=context) + if metaclass is not None: + dunder_call = next(metaclass.igetattr("__call__", context)) + except exceptions.AttributeInferenceError: + pass + + if dunder_call and dunder_call.qname() != "builtins.type.__call__": + # Call type.__call__ if not set metaclass + # (since type is the default metaclass) + context = contextmod.bind_context_to_node(context, self) + yield from dunder_call.infer_call_result(caller, context) + else: + yield self.instantiate_class() + + def scope_lookup(self, node, name, offset=0): + """Lookup where the given name is assigned. + + :param node: The node to look for assignments up to. + Any assignments after the given node are ignored. + :type node: NodeNG + + :param name: The name to find assignments for. + :type name: str + + :param offset: The line offset to filter statements up to. + :type offset: int + + :returns: This scope node and the list of assignments associated to the + given name according to the scope where it has been found (locals, + globals or builtin). + :rtype: tuple(str, list(NodeNG)) + """ + # If the name looks like a builtin name, just try to look + # into the upper scope of this class. We might have a + # decorator that it's poorly named after a builtin object + # inside this class. + lookup_upper_frame = ( + isinstance(node.parent, node_classes.Decorators) + and name in MANAGER.builtins_module + ) + if ( + any(node == base or base.parent_of(node) for base in self.bases) + or lookup_upper_frame + ): + # Handle the case where we have either a name + # in the bases of a class, which exists before + # the actual definition or the case where we have + # a Getattr node, with that name. + # + # name = ... + # class A(name): + # def name(self): ... + # + # import name + # class A(name.Name): + # def name(self): ... + + frame = self.parent.frame() + # line offset to avoid that class A(A) resolve the ancestor to + # the defined class + offset = -1 + else: + frame = self + return frame._scope_lookup(node, name, offset) + + @property + def basenames(self): + """The names of the parent classes + + Names are given in the order they appear in the class definition. + + :type: list(str) + """ + return [bnode.as_string() for bnode in self.bases] + + def ancestors(self, recurs=True, context=None): + """Iterate over the base classes in prefixed depth first order. + + :param recurs: Whether to recurse or return direct ancestors only. + :type recurs: bool + + :returns: The base classes + :rtype: iterable(NodeNG) + """ + # FIXME: should be possible to choose the resolution order + # FIXME: inference make infinite loops possible here + yielded = {self} + if context is None: + context = contextmod.InferenceContext() + if not self.bases and self.qname() != "builtins.object": + yield builtin_lookup("object")[1][0] + return + + for stmt in self.bases: + with context.restore_path(): + try: + for baseobj in stmt.infer(context): + if not isinstance(baseobj, ClassDef): + if isinstance(baseobj, bases.Instance): + baseobj = baseobj._proxied + else: + continue + if not baseobj.hide: + if baseobj in yielded: + continue + yielded.add(baseobj) + yield baseobj + if not recurs: + continue + for grandpa in baseobj.ancestors(recurs=True, context=context): + if grandpa is self: + # This class is the ancestor of itself. + break + if grandpa in yielded: + continue + yielded.add(grandpa) + yield grandpa + except exceptions.InferenceError: + continue + + def local_attr_ancestors(self, name, context=None): + """Iterate over the parents that define the given name. + + :param name: The name to find definitions for. + :type name: str + + :returns: The parents that define the given name. + :rtype: iterable(NodeNG) + """ + # Look up in the mro if we can. This will result in the + # attribute being looked up just as Python does it. + try: + ancestors = self.mro(context)[1:] + except exceptions.MroError: + # Fallback to use ancestors, we can't determine + # a sane MRO. + ancestors = self.ancestors(context=context) + for astroid in ancestors: + if name in astroid: + yield astroid + + def instance_attr_ancestors(self, name, context=None): + """Iterate over the parents that define the given name as an attribute. + + :param name: The name to find definitions for. + :type name: str + + :returns: The parents that define the given name as + an instance attribute. + :rtype: iterable(NodeNG) + """ + for astroid in self.ancestors(context=context): + if name in astroid.instance_attrs: + yield astroid + + def has_base(self, node): + """Whether this class directly inherits from the given node. + + :param node: The node to check for. + :type node: NodeNG + + :returns: True if this class directly inherits from the given node. + :rtype: bool + """ + return node in self.bases + + def local_attr(self, name, context=None): + """Get the list of assign nodes associated to the given name. + + Assignments are looked for in both this class and in parents. + + :returns: The list of assignments to the given name. + :rtype: list(NodeNG) + + :raises AttributeInferenceError: If no attribute with this name + can be found in this class or parent classes. + """ + result = [] + if name in self.locals: + result = self.locals[name] + else: + class_node = next(self.local_attr_ancestors(name, context), None) + if class_node: + result = class_node.locals[name] + result = [n for n in result if not isinstance(n, node_classes.DelAttr)] + if result: + return result + raise exceptions.AttributeInferenceError( + target=self, attribute=name, context=context + ) + + def instance_attr(self, name, context=None): + """Get the list of nodes associated to the given attribute name. + + Assignments are looked for in both this class and in parents. + + :returns: The list of assignments to the given name. + :rtype: list(NodeNG) + + :raises AttributeInferenceError: If no attribute with this name + can be found in this class or parent classes. + """ + # Return a copy, so we don't modify self.instance_attrs, + # which could lead to infinite loop. + values = list(self.instance_attrs.get(name, [])) + # get all values from parents + for class_node in self.instance_attr_ancestors(name, context): + values += class_node.instance_attrs[name] + values = [n for n in values if not isinstance(n, node_classes.DelAttr)] + if values: + return values + raise exceptions.AttributeInferenceError( + target=self, attribute=name, context=context + ) + + def instantiate_class(self): + """Get an :class:`Instance` of the :class:`ClassDef` node. + + :returns: An :class:`Instance` of the :class:`ClassDef` node, + or self if this is not possible. + :rtype: Instance or ClassDef + """ + try: + if any(cls.name in EXCEPTION_BASE_CLASSES for cls in self.mro()): + # Subclasses of exceptions can be exception instances + return objects.ExceptionInstance(self) + except exceptions.MroError: + pass + return bases.Instance(self) + + def getattr(self, name, context=None, class_context=True): + """Get an attribute from this class, using Python's attribute semantic. + + This method doesn't look in the :attr:`instance_attrs` dictionary + since it is done by an :class:`Instance` proxy at inference time. + It may return an :class:`Uninferable` object if + the attribute has not been + found, but a ``__getattr__`` or ``__getattribute__`` method is defined. + If ``class_context`` is given, then it is considered that the + attribute is accessed from a class context, + e.g. ClassDef.attribute, otherwise it might have been accessed + from an instance as well. If ``class_context`` is used in that + case, then a lookup in the implicit metaclass and the explicit + metaclass will be done. + + :param name: The attribute to look for. + :type name: str + + :param class_context: Whether the attribute can be accessed statically. + :type class_context: bool + + :returns: The attribute. + :rtype: list(NodeNG) + + :raises AttributeInferenceError: If the attribute cannot be inferred. + """ + if not name: + raise exceptions.AttributeInferenceError( + target=self, attribute=name, context=context + ) + + values = self.locals.get(name, []) + if name in self.special_attributes and class_context and not values: + result = [self.special_attributes.lookup(name)] + if name == "__bases__": + # Need special treatment, since they are mutable + # and we need to return all the values. + result += values + return result + + # don't modify the list in self.locals! + values = list(values) + for classnode in self.ancestors(recurs=True, context=context): + values += classnode.locals.get(name, []) + + if class_context: + values += self._metaclass_lookup_attribute(name, context) + + if not values: + raise exceptions.AttributeInferenceError( + target=self, attribute=name, context=context + ) + + # Look for AnnAssigns, which are not attributes in the purest sense. + for value in values: + if isinstance(value, node_classes.AssignName): + stmt = value.statement() + if isinstance(stmt, node_classes.AnnAssign) and stmt.value is None: + raise exceptions.AttributeInferenceError( + target=self, attribute=name, context=context + ) + return values + + def _metaclass_lookup_attribute(self, name, context): + """Search the given name in the implicit and the explicit metaclass.""" + attrs = set() + implicit_meta = self.implicit_metaclass() + context = contextmod.copy_context(context) + metaclass = self.metaclass(context=context) + for cls in {implicit_meta, metaclass}: + if cls and cls != self and isinstance(cls, ClassDef): + cls_attributes = self._get_attribute_from_metaclass(cls, name, context) + attrs.update(set(cls_attributes)) + return attrs + + def _get_attribute_from_metaclass(self, cls, name, context): + try: + attrs = cls.getattr(name, context=context, class_context=True) + except exceptions.AttributeInferenceError: + return + + for attr in bases._infer_stmts(attrs, context, frame=cls): + if not isinstance(attr, FunctionDef): + yield attr + continue + + if isinstance(attr, objects.Property): + yield attr + continue + if attr.type == "classmethod": + # If the method is a classmethod, then it will + # be bound to the metaclass, not to the class + # from where the attribute is retrieved. + # get_wrapping_class could return None, so just + # default to the current class. + frame = get_wrapping_class(attr) or self + yield bases.BoundMethod(attr, frame) + elif attr.type == "staticmethod": + yield attr + else: + yield bases.BoundMethod(attr, self) + + def igetattr(self, name, context=None, class_context=True): + """Infer the possible values of the given variable. + + :param name: The name of the variable to infer. + :type name: str + + :returns: The inferred possible values. + :rtype: iterable(NodeNG or Uninferable) + """ + # set lookup name since this is necessary to infer on import nodes for + # instance + context = contextmod.copy_context(context) + context.lookupname = name + + metaclass = self.declared_metaclass(context=context) + try: + attributes = self.getattr(name, context, class_context=class_context) + # If we have more than one attribute, make sure that those starting from + # the second one are from the same scope. This is to account for modifications + # to the attribute happening *after* the attribute's definition (e.g. AugAssigns on lists) + if len(attributes) > 1: + first_attr, attributes = attributes[0], attributes[1:] + first_scope = first_attr.scope() + attributes = [first_attr] + [ + attr + for attr in attributes + if attr.parent and attr.parent.scope() == first_scope + ] + + for inferred in bases._infer_stmts(attributes, context, frame=self): + # yield Uninferable object instead of descriptors when necessary + if not isinstance(inferred, node_classes.Const) and isinstance( + inferred, bases.Instance + ): + try: + inferred._proxied.getattr("__get__", context) + except exceptions.AttributeInferenceError: + yield inferred + else: + yield util.Uninferable + elif isinstance(inferred, objects.Property): + function = inferred.function + if not class_context: + # Through an instance so we can solve the property + yield from function.infer_call_result( + caller=self, context=context + ) + # If we have a metaclass, we're accessing this attribute through + # the class itself, which means we can solve the property + elif metaclass: + # Resolve a property as long as it is not accessed through + # the class itself. + yield from function.infer_call_result( + caller=self, context=context + ) + else: + yield inferred + else: + yield function_to_method(inferred, self) + except exceptions.AttributeInferenceError as error: + if not name.startswith("__") and self.has_dynamic_getattr(context): + # class handle some dynamic attributes, return a Uninferable object + yield util.Uninferable + else: + raise exceptions.InferenceError( + error.message, target=self, attribute=name, context=context + ) + + def has_dynamic_getattr(self, context=None): + """Check if the class has a custom __getattr__ or __getattribute__. + + If any such method is found and it is not from + builtins, nor from an extension module, then the function + will return True. + + :returns: True if the class has a custom + __getattr__ or __getattribute__, False otherwise. + :rtype: bool + """ + + def _valid_getattr(node): + root = node.root() + return root.name != BUILTINS and getattr(root, "pure_python", None) + + try: + return _valid_getattr(self.getattr("__getattr__", context)[0]) + except exceptions.AttributeInferenceError: + # if self.newstyle: XXX cause an infinite recursion error + try: + getattribute = self.getattr("__getattribute__", context)[0] + return _valid_getattr(getattribute) + except exceptions.AttributeInferenceError: + pass + return False + + def getitem(self, index, context=None): + """Return the inference of a subscript. + + This is basically looking up the method in the metaclass and calling it. + + :returns: The inferred value of a subscript to this class. + :rtype: NodeNG + + :raises AstroidTypeError: If this class does not define a + ``__getitem__`` method. + """ + try: + methods = dunder_lookup.lookup(self, "__getitem__") + except exceptions.AttributeInferenceError as exc: + raise exceptions.AstroidTypeError(node=self, context=context) from exc + + method = methods[0] + + # Create a new callcontext for providing index as an argument. + new_context = contextmod.bind_context_to_node(context, self) + new_context.callcontext = contextmod.CallContext(args=[index]) + + try: + return next(method.infer_call_result(self, new_context)) + except exceptions.InferenceError: + return util.Uninferable + + def methods(self): + """Iterate over all of the method defined in this class and its parents. + + :returns: The methods defined on the class. + :rtype: iterable(FunctionDef) + """ + done = {} + for astroid in itertools.chain(iter((self,)), self.ancestors()): + for meth in astroid.mymethods(): + if meth.name in done: + continue + done[meth.name] = None + yield meth + + def mymethods(self): + """Iterate over all of the method defined in this class only. + + :returns: The methods defined on the class. + :rtype: iterable(FunctionDef) + """ + for member in self.values(): + if isinstance(member, FunctionDef): + yield member + + def implicit_metaclass(self): + """Get the implicit metaclass of the current class. + + For newstyle classes, this will return an instance of builtins.type. + For oldstyle classes, it will simply return None, since there's + no implicit metaclass there. + + :returns: The metaclass. + :rtype: builtins.type or None + """ + if self.newstyle: + return builtin_lookup("type")[1][0] + return None + + _metaclass = None + + def declared_metaclass(self, context=None): + """Return the explicit declared metaclass for the current class. + + An explicit declared metaclass is defined + either by passing the ``metaclass`` keyword argument + in the class definition line (Python 3) or (Python 2) by + having a ``__metaclass__`` class attribute, or if there are + no explicit bases but there is a global ``__metaclass__`` variable. + + :returns: The metaclass of this class, + or None if one could not be found. + :rtype: NodeNG or None + """ + for base in self.bases: + try: + for baseobj in base.infer(context=context): + if isinstance(baseobj, ClassDef) and baseobj.hide: + self._metaclass = baseobj._metaclass + self._metaclass_hack = True + break + except exceptions.InferenceError: + pass + + if self._metaclass: + # Expects this from Py3k TreeRebuilder + try: + return next( + node + for node in self._metaclass.infer(context=context) + if node is not util.Uninferable + ) + except (exceptions.InferenceError, StopIteration): + return None + + return None + + def _find_metaclass(self, seen=None, context=None): + if seen is None: + seen = set() + seen.add(self) + + klass = self.declared_metaclass(context=context) + if klass is None: + for parent in self.ancestors(context=context): + if parent not in seen: + klass = parent._find_metaclass(seen) + if klass is not None: + break + return klass + + def metaclass(self, context=None): + """Get the metaclass of this class. + + If this class does not define explicitly a metaclass, + then the first defined metaclass in ancestors will be used + instead. + + :returns: The metaclass of this class. + :rtype: NodeNG or None + """ + return self._find_metaclass(context=context) + + def has_metaclass_hack(self): + return self._metaclass_hack + + def _islots(self): + """ Return an iterator with the inferred slots. """ + if "__slots__" not in self.locals: + return None + for slots in self.igetattr("__slots__"): + # check if __slots__ is a valid type + for meth in ITER_METHODS: + try: + slots.getattr(meth) + break + except exceptions.AttributeInferenceError: + continue + else: + continue + + if isinstance(slots, node_classes.Const): + # a string. Ignore the following checks, + # but yield the node, only if it has a value + if slots.value: + yield slots + continue + if not hasattr(slots, "itered"): + # we can't obtain the values, maybe a .deque? + continue + + if isinstance(slots, node_classes.Dict): + values = [item[0] for item in slots.items] + else: + values = slots.itered() + if values is util.Uninferable: + continue + if not values: + # Stop the iteration, because the class + # has an empty list of slots. + return values + + for elt in values: + try: + for inferred in elt.infer(): + if inferred is util.Uninferable: + continue + if not isinstance( + inferred, node_classes.Const + ) or not isinstance(inferred.value, str): + continue + if not inferred.value: + continue + yield inferred + except exceptions.InferenceError: + continue + + return None + + def _slots(self): + if not self.newstyle: + raise NotImplementedError( + "The concept of slots is undefined for old-style classes." + ) + + slots = self._islots() + try: + first = next(slots) + except StopIteration as exc: + # The class doesn't have a __slots__ definition or empty slots. + if exc.args and exc.args[0] not in ("", None): + return exc.args[0] + return None + return [first] + list(slots) + + # Cached, because inferring them all the time is expensive + @decorators_mod.cached + def slots(self): + """Get all the slots for this node. + + :returns: The names of slots for this class. + If the class doesn't define any slot, through the ``__slots__`` + variable, then this function will return a None. + Also, it will return None in the case the slots were not inferred. + :rtype: list(str) or None + """ + + def grouped_slots(): + # Not interested in object, since it can't have slots. + for cls in self.mro()[:-1]: + try: + cls_slots = cls._slots() + except NotImplementedError: + continue + if cls_slots is not None: + yield from cls_slots + else: + yield None + + if not self.newstyle: + raise NotImplementedError( + "The concept of slots is undefined for old-style classes." + ) + + slots = list(grouped_slots()) + if not all(slot is not None for slot in slots): + return None + + return sorted(set(slots), key=lambda item: item.value) + + def _inferred_bases(self, context=None): + # Similar with .ancestors, but the difference is when one base is inferred, + # only the first object is wanted. That's because + # we aren't interested in superclasses, as in the following + # example: + # + # class SomeSuperClass(object): pass + # class SomeClass(SomeSuperClass): pass + # class Test(SomeClass): pass + # + # Inferring SomeClass from the Test's bases will give + # us both SomeClass and SomeSuperClass, but we are interested + # only in SomeClass. + + if context is None: + context = contextmod.InferenceContext() + if not self.bases and self.qname() != "builtins.object": + yield builtin_lookup("object")[1][0] + return + + for stmt in self.bases: + try: + baseobj = next(stmt.infer(context=context)) + except exceptions.InferenceError: + continue + if isinstance(baseobj, bases.Instance): + baseobj = baseobj._proxied + if not isinstance(baseobj, ClassDef): + continue + if not baseobj.hide: + yield baseobj + else: + yield from baseobj.bases + + def _compute_mro(self, context=None): + inferred_bases = list(self._inferred_bases(context=context)) + bases_mro = [] + for base in inferred_bases: + if base is self: + continue + + try: + mro = base._compute_mro(context=context) + bases_mro.append(mro) + except NotImplementedError: + # Some classes have in their ancestors both newstyle and + # old style classes. For these we can't retrieve the .mro, + # although in Python it's possible, since the class we are + # currently working is in fact new style. + # So, we fallback to ancestors here. + ancestors = list(base.ancestors(context=context)) + bases_mro.append(ancestors) + + unmerged_mro = [[self]] + bases_mro + [inferred_bases] + unmerged_mro = list(clean_duplicates_mro(unmerged_mro, self, context)) + return _c3_merge(unmerged_mro, self, context) + + def mro(self, context=None) -> List["ClassDef"]: + """Get the method resolution order, using C3 linearization. + + :returns: The list of ancestors, sorted by the mro. + :rtype: list(NodeNG) + :raises DuplicateBasesError: Duplicate bases in the same class base + :raises InconsistentMroError: A class' MRO is inconsistent + """ + return self._compute_mro(context=context) + + def bool_value(self, context=None): + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + For a :class:`ClassDef` this is always ``True``. + :rtype: bool + """ + return True + + def get_children(self): + if self.decorators is not None: + yield self.decorators + + yield from self.bases + yield from self.body + + @decorators_mod.cached + def _get_assign_nodes(self): + children_assign_nodes = ( + child_node._get_assign_nodes() for child_node in self.body + ) + return list(itertools.chain.from_iterable(children_assign_nodes)) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/test_utils.py b/Display/.venv/lib/python3.7/site-packages/astroid/test_utils.py new file mode 100644 index 0000000..e22c7a4 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/test_utils.py @@ -0,0 +1,73 @@ +# Copyright (c) 2013-2014 Google, Inc. +# Copyright (c) 2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2015-2016, 2018-2019 Claudiu Popa +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2016 Jakub Wilk +# Copyright (c) 2018 Anthony Sottile + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +"""Utility functions for test code that uses astroid ASTs as input.""" +import contextlib +import functools +import sys +import warnings + +import pytest + +from astroid import nodes + + +def require_version(minver=None, maxver=None): + """ Compare version of python interpreter to the given one. Skip the test + if older. + """ + + def parse(string, default=None): + string = string or default + try: + return tuple(int(v) for v in string.split(".")) + except ValueError as exc: + raise ValueError( + "{string} is not a correct version : should be X.Y[.Z].".format( + string=string + ) + ) from exc + + def check_require_version(f): + current = sys.version_info[:3] + if parse(minver, "0") < current <= parse(maxver, "4"): + return f + + str_version = ".".join(str(v) for v in sys.version_info) + + @functools.wraps(f) + def new_f(*args, **kwargs): + if minver is not None: + pytest.skip( + "Needs Python > %s. Current version is %s." % (minver, str_version) + ) + elif maxver is not None: + pytest.skip( + "Needs Python <= %s. Current version is %s." % (maxver, str_version) + ) + + return new_f + + return check_require_version + + +def get_name_node(start_from, name, index=0): + return [n for n in start_from.nodes_of_class(nodes.Name) if n.name == name][index] + + +@contextlib.contextmanager +def enable_warning(warning): + warnings.simplefilter("always", warning) + try: + yield + finally: + # Reset it to default value, so it will take + # into account the values from the -W flag. + warnings.simplefilter("default", warning) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/transforms.py b/Display/.venv/lib/python3.7/site-packages/astroid/transforms.py new file mode 100644 index 0000000..e5506cc --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/transforms.py @@ -0,0 +1,90 @@ +# Copyright (c) 2015-2016, 2018 Claudiu Popa +# Copyright (c) 2016 Ceridwen +# Copyright (c) 2018 Nick Drozd + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + + +import collections +from functools import lru_cache + + +class TransformVisitor: + """A visitor for handling transforms. + + The standard approach of using it is to call + :meth:`~visit` with an *astroid* module and the class + will take care of the rest, walking the tree and running the + transforms for each encountered node. + """ + + TRANSFORM_MAX_CACHE_SIZE = 10000 + + def __init__(self): + self.transforms = collections.defaultdict(list) + + @lru_cache(maxsize=TRANSFORM_MAX_CACHE_SIZE) + def _transform(self, node): + """Call matching transforms for the given node if any and return the + transformed node. + """ + cls = node.__class__ + if cls not in self.transforms: + # no transform registered for this class of node + return node + + transforms = self.transforms[cls] + for transform_func, predicate in transforms: + if predicate is None or predicate(node): + ret = transform_func(node) + # if the transformation function returns something, it's + # expected to be a replacement for the node + if ret is not None: + node = ret + if ret.__class__ != cls: + # Can no longer apply the rest of the transforms. + break + return node + + def _visit(self, node): + if hasattr(node, "_astroid_fields"): + for name in node._astroid_fields: + value = getattr(node, name) + visited = self._visit_generic(value) + if visited != value: + setattr(node, name, visited) + return self._transform(node) + + def _visit_generic(self, node): + if isinstance(node, list): + return [self._visit_generic(child) for child in node] + if isinstance(node, tuple): + return tuple(self._visit_generic(child) for child in node) + if not node or isinstance(node, str): + return node + + return self._visit(node) + + def register_transform(self, node_class, transform, predicate=None): + """Register `transform(node)` function to be applied on the given + astroid's `node_class` if `predicate` is None or returns true + when called with the node as argument. + + The transform function may return a value which is then used to + substitute the original node in the tree. + """ + self.transforms[node_class].append((transform, predicate)) + + def unregister_transform(self, node_class, transform, predicate=None): + """Unregister the given transform.""" + self.transforms[node_class].remove((transform, predicate)) + + def visit(self, module): + """Walk the given astroid *tree* and transform each encountered node + + Only the nodes which have transforms registered will actually + be replaced or changed. + """ + module.body = [self._visit(child) for child in module.body] + return self._transform(module) diff --git a/Display/.venv/lib/python3.7/site-packages/astroid/util.py b/Display/.venv/lib/python3.7/site-packages/astroid/util.py new file mode 100644 index 0000000..3ab7561 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/astroid/util.py @@ -0,0 +1,164 @@ +# Copyright (c) 2015-2018 Claudiu Popa +# Copyright (c) 2015-2016 Ceridwen +# Copyright (c) 2018 Bryce Guinta +# Copyright (c) 2018 Nick Drozd + +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER + +import warnings +from itertools import islice + +import importlib +import lazy_object_proxy + + +def lazy_descriptor(obj): + class DescriptorProxy(lazy_object_proxy.Proxy): + def __get__(self, instance, owner=None): + return self.__class__.__get__(self, instance) + + return DescriptorProxy(obj) + + +def lazy_import(module_name): + return lazy_object_proxy.Proxy( + lambda: importlib.import_module("." + module_name, "astroid") + ) + + +@object.__new__ +class Uninferable: + """Special inference object, which is returned when inference fails.""" + + def __repr__(self): + return "Uninferable" + + __str__ = __repr__ + + def __getattribute__(self, name): + if name == "next": + raise AttributeError("next method should not be called") + if name.startswith("__") and name.endswith("__"): + return object.__getattribute__(self, name) + if name == "accept": + return object.__getattribute__(self, name) + return self + + def __call__(self, *args, **kwargs): + return self + + def __bool__(self): + return False + + __nonzero__ = __bool__ + + def accept(self, visitor): + func = getattr(visitor, "visit_uninferable") + return func(self) + + +class BadOperationMessage: + """Object which describes a TypeError occurred somewhere in the inference chain + + This is not an exception, but a container object which holds the types and + the error which occurred. + """ + + +class BadUnaryOperationMessage(BadOperationMessage): + """Object which describes operational failures on UnaryOps.""" + + def __init__(self, operand, op, error): + self.operand = operand + self.op = op + self.error = error + + @property + def _object_type_helper(self): + helpers = lazy_import("helpers") + return helpers.object_type + + def _object_type(self, obj): + # pylint: disable=not-callable; can't infer lazy_import + objtype = self._object_type_helper(obj) + if objtype is Uninferable: + return None + + return objtype + + def __str__(self): + if hasattr(self.operand, "name"): + operand_type = self.operand.name + else: + object_type = self._object_type(self.operand) + if hasattr(object_type, "name"): + operand_type = object_type.name + else: + # Just fallback to as_string + operand_type = object_type.as_string() + + msg = "bad operand type for unary {}: {}" + return msg.format(self.op, operand_type) + + +class BadBinaryOperationMessage(BadOperationMessage): + """Object which describes type errors for BinOps.""" + + def __init__(self, left_type, op, right_type): + self.left_type = left_type + self.right_type = right_type + self.op = op + + def __str__(self): + msg = "unsupported operand type(s) for {}: {!r} and {!r}" + return msg.format(self.op, self.left_type.name, self.right_type.name) + + +def _instancecheck(cls, other): + wrapped = cls.__wrapped__ + other_cls = other.__class__ + is_instance_of = wrapped is other_cls or issubclass(other_cls, wrapped) + warnings.warn( + "%r is deprecated and slated for removal in astroid " + "2.0, use %r instead" % (cls.__class__.__name__, wrapped.__name__), + PendingDeprecationWarning, + stacklevel=2, + ) + return is_instance_of + + +def proxy_alias(alias_name, node_type): + """Get a Proxy from the given name to the given node type.""" + proxy = type( + alias_name, + (lazy_object_proxy.Proxy,), + { + "__class__": object.__dict__["__class__"], + "__instancecheck__": _instancecheck, + }, + ) + return proxy(lambda: node_type) + + +def limit_inference(iterator, size): + """Limit inference amount. + + Limit inference amount to help with performance issues with + exponentially exploding possible results. + + :param iterator: Inference generator to limit + :type iterator: Iterator(NodeNG) + + :param size: Maximum mount of nodes yielded plus an + Uninferable at the end if limit reached + :type size: int + + :yields: A possibly modified generator + :rtype param: Iterable + """ + yield from islice(iterator, size) + has_more = next(iterator, False) + if has_more is not False: + yield Uninferable + return diff --git a/Display/.venv/lib/python3.7/site-packages/easy_install.py b/Display/.venv/lib/python3.7/site-packages/easy_install.py new file mode 100644 index 0000000..d87e984 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/easy_install.py @@ -0,0 +1,5 @@ +"""Run the EasyInstall command""" + +if __name__ == '__main__': + from setuptools.command.easy_install import main + main() diff --git a/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/INSTALLER b/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/LICENSE b/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/LICENSE new file mode 100755 index 0000000..b5083a5 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Timothy Edmund Crosley + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/METADATA b/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/METADATA new file mode 100644 index 0000000..fbc7f6c --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/METADATA @@ -0,0 +1,697 @@ +Metadata-Version: 2.1 +Name: isort +Version: 4.3.21 +Summary: A Python utility / library to sort Python imports. +Home-page: https://github.com/timothycrosley/isort +Author: Timothy Crosley +Author-email: timothy.crosley@gmail.com +License: MIT +Keywords: Refactor,Python,Python2,Python3,Refactoring,Imports,Sort,Clean +Platform: UNKNOWN +Classifier: Development Status :: 6 - Mature +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: Environment :: Console +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Utilities +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* +Requires-Dist: futures ; python_version < "3.2" +Requires-Dist: backports.functools-lru-cache ; python_version < "3.2" +Provides-Extra: pipfile +Requires-Dist: pipreqs ; extra == 'pipfile' +Requires-Dist: requirementslib ; extra == 'pipfile' +Provides-Extra: pyproject +Requires-Dist: toml ; extra == 'pyproject' +Provides-Extra: requirements +Requires-Dist: pipreqs ; extra == 'requirements' +Requires-Dist: pip-api ; extra == 'requirements' +Provides-Extra: xdg_home +Requires-Dist: appdirs (>=1.4.0) ; extra == 'xdg_home' + +.. image:: https://raw.github.com/timothycrosley/isort/master/logo.png + :alt: isort + +######## + +.. image:: https://badge.fury.io/py/isort.svg + :target: https://badge.fury.io/py/isort + :alt: PyPI version + +.. image:: https://travis-ci.org/timothycrosley/isort.svg?branch=master + :target: https://travis-ci.org/timothycrosley/isort + :alt: Build Status + + +.. image:: https://coveralls.io/repos/timothycrosley/isort/badge.svg?branch=release%2F2.6.0&service=github + :target: https://coveralls.io/github/timothycrosley/isort?branch=release%2F2.6.0 + :alt: Coverage + +.. image:: https://img.shields.io/github/license/mashape/apistatus.svg + :target: https://pypi.org/project/hug/ + :alt: License + +.. image:: https://badges.gitter.im/Join%20Chat.svg + :alt: Join the chat at https://gitter.im/timothycrosley/isort + :target: https://gitter.im/timothycrosley/isort?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge + +.. image:: https://pepy.tech/badge/isort + :alt: Downloads + :target: https://pepy.tech/project/isort + +isort your python imports for you so you don't have to. + +isort is a Python utility / library to sort imports alphabetically, and automatically separated into sections. +It provides a command line utility, Python library and `plugins for various editors `_ to quickly sort all your imports. +It currently cleanly supports Python 2.7 and 3.4+ without any dependencies. + +.. image:: https://raw.github.com/timothycrosley/isort/develop/example.gif + :alt: Example Usage + +Before isort: + +.. code-block:: python + + from my_lib import Object + + print("Hey") + + import os + + from my_lib import Object3 + + from my_lib import Object2 + + import sys + + from third_party import lib15, lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14 + + import sys + + from __future__ import absolute_import + + from third_party import lib3 + + print("yo") + +After isort: + +.. code-block:: python + + from __future__ import absolute_import + + import os + import sys + + from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, + lib9, lib10, lib11, lib12, lib13, lib14, lib15) + + from my_lib import Object, Object2, Object3 + + print("Hey") + print("yo") + +Installing isort +================ + +Installing isort is as simple as: + +.. code-block:: bash + + pip install isort + +Install isort with requirements.txt support: + +.. code-block:: bash + + pip install isort[requirements] + +Install isort with Pipfile support: + +.. code-block:: bash + + pip install isort[pipfile] + +Install isort with both formats support: + +.. code-block:: bash + + pip install isort[requirements,pipfile] + +Using isort +=========== + +**From the command line**: + +.. code-block:: bash + + isort mypythonfile.py mypythonfile2.py + +or recursively: + +.. code-block:: bash + + isort -rc . + +*which is equivalent to:* + +.. code-block:: bash + + isort **/*.py + +or to see the proposed changes without applying them: + +.. code-block:: bash + + isort mypythonfile.py --diff + +Finally, to atomically run isort against a project, only applying changes if they don't introduce syntax errors do: + +.. code-block:: bash + + isort -rc --atomic . + +(Note: this is disabled by default as it keeps isort from being able to run against code written using a different version of Python) + +**From within Python**: + +.. code-block:: bash + + from isort import SortImports + + SortImports("pythonfile.py") + +or: + +.. code-block:: bash + + from isort import SortImports + + new_contents = SortImports(file_contents=old_contents).output + +**From within Kate:** + +.. code-block:: bash + + ctrl+[ + +or: + +.. code-block:: bash + + menu > Python > Sort Imports + +Installing isort's Kate plugin +============================== + +For KDE 4.13+ / Pate 2.0+: + +.. code-block:: bash + + wget https://raw.github.com/timothycrosley/isort/master/kate_plugin/isort_plugin.py --output-document ~/.kde/share/apps/kate/pate/isort_plugin.py + wget https://raw.github.com/timothycrosley/isort/master/kate_plugin/isort_plugin_ui.rc --output-document ~/.kde/share/apps/kate/pate/isort_plugin_ui.rc + wget https://raw.github.com/timothycrosley/isort/master/kate_plugin/katepart_isort.desktop --output-document ~/.kde/share/kde4/services/katepart_isort.desktop + +For all older versions: + +.. code-block:: bash + + wget https://raw.github.com/timothycrosley/isort/master/kate_plugin/isort_plugin_old.py --output-document ~/.kde/share/apps/kate/pate/isort_plugin.py + +You will then need to restart kate and enable Python Plugins as well as the isort plugin itself. + +Installing isort's for your preferred text editor +================================================= + +Several plugins have been written that enable to use isort from within a variety of text-editors. +You can find a full list of them `on the isort wiki `_. +Additionally, I will enthusiastically accept pull requests that include plugins for other text editors +and add documentation for them as I am notified. + +How does isort work? +==================== + +isort parses specified files for global level import lines (imports outside of try / except blocks, functions, etc..) +and puts them all at the top of the file grouped together by the type of import: + +- Future +- Python Standard Library +- Third Party +- Current Python Project +- Explicitly Local (. before import, as in: ``from . import x``) +- Custom Separate Sections (Defined by forced_separate list in configuration file) +- Custom Sections (Defined by sections list in configuration file) + +Inside of each section the imports are sorted alphabetically. isort automatically removes duplicate python imports, +and wraps long from imports to the specified line length (defaults to 79). + +When will isort not work? +========================= + +If you ever have the situation where you need to have a try / except block in the middle of top-level imports or if +your import order is directly linked to precedence. + +For example: a common practice in Django settings files is importing * from various settings files to form +a new settings file. In this case if any of the imports change order you are changing the settings definition itself. + +However, you can configure isort to skip over just these files - or even to force certain imports to the top. + +Configuring isort +================= + +If you find the default isort settings do not work well for your project, isort provides several ways to adjust +the behavior. + +To configure isort for a single user create a ``~/.isort.cfg`` or ``$XDG_CONFIG_HOME/isort.cfg`` file: + +.. code-block:: ini + + [settings] + line_length=120 + force_to_top=file1.py,file2.py + skip=file3.py,file4.py + known_future_library=future,pies + known_standard_library=std,std2 + known_third_party=randomthirdparty + known_first_party=mylib1,mylib2 + indent=' ' + multi_line_output=3 + length_sort=1 + forced_separate=django.contrib,django.utils + default_section=FIRSTPARTY + no_lines_before=LOCALFOLDER + +Additionally, you can specify project level configuration simply by placing a ``.isort.cfg`` file at the root of your +project. isort will look up to 25 directories up, from the file it is ran against, to find a project specific configuration. + +Or, if you prefer, you can add an ``isort`` or ``tool:isort`` section to your project's ``setup.cfg`` or ``tox.ini`` file with any desired settings. + +You can also add your desired settings under a ``[tool.isort]`` section in your ``pyproject.toml`` file. + +You can then override any of these settings by using command line arguments, or by passing in override values to the +SortImports class. + +Finally, as of version 3.0 isort supports editorconfig files using the standard syntax defined here: +https://editorconfig.org/ + +Meaning you place any standard isort configuration parameters within a .editorconfig file under the ``*.py`` section +and they will be honored. + +For a full list of isort settings and their meanings `take a look at the isort wiki `_. + +Multi line output modes +======================= + +You will notice above the "multi_line_output" setting. This setting defines how from imports wrap when they extend +past the line_length limit and has 6 possible settings: + +**0 - Grid** + +.. code-block:: python + + from third_party import (lib1, lib2, lib3, + lib4, lib5, ...) + +**1 - Vertical** + +.. code-block:: python + + from third_party import (lib1, + lib2, + lib3 + lib4, + lib5, + ...) + +**2 - Hanging Indent** + +.. code-block:: python + + from third_party import \ + lib1, lib2, lib3, \ + lib4, lib5, lib6 + +**3 - Vertical Hanging Indent** + +.. code-block:: python + + from third_party import ( + lib1, + lib2, + lib3, + lib4, + ) + +**4 - Hanging Grid** + +.. code-block:: python + + from third_party import ( + lib1, lib2, lib3, lib4, + lib5, ...) + +**5 - Hanging Grid Grouped** + +.. code-block:: python + + from third_party import ( + lib1, lib2, lib3, lib4, + lib5, ... + ) + +**6 - Hanging Grid Grouped, No Trailing Comma** + +In Mode 5 isort leaves a single extra space to maintain consistency of output when a comma is added at the end. +Mode 6 is the same - except that no extra space is maintained leading to the possibility of lines one character longer. +You can enforce a trailing comma by using this in conjunction with `-tc` or `trailing_comma: True`. + +.. code-block:: python + + from third_party import ( + lib1, lib2, lib3, lib4, + lib5 + ) + +**7 - NOQA** + +.. code-block:: python + + from third_party import lib1, lib2, lib3, ... # NOQA + +Alternatively, you can set ``force_single_line`` to ``True`` (``-sl`` on the command line) and every import will appear on its +own line: + +.. code-block:: python + + from third_party import lib1 + from third_party import lib2 + from third_party import lib3 + ... + +Note: to change the how constant indents appear - simply change the indent property with the following accepted formats: + +* Number of spaces you would like. For example: 4 would cause standard 4 space indentation. +* Tab +* A verbatim string with quotes around it. + +For example: + +.. code-block:: python + + " " + +is equivalent to 4. + +For the import styles that use parentheses, you can control whether or not to +include a trailing comma after the last import with the ``include_trailing_comma`` +option (defaults to ``False``). + +Intelligently Balanced Multi-line Imports +========================================= + +As of isort 3.1.0 support for balanced multi-line imports has been added. +With this enabled isort will dynamically change the import length to the one that produces the most balanced grid, +while staying below the maximum import length defined. + +Example: + +.. code-block:: python + + from __future__ import (absolute_import, division, + print_function, unicode_literals) + +Will be produced instead of: + +.. code-block:: python + + from __future__ import (absolute_import, division, print_function, + unicode_literals) + +To enable this set ``balanced_wrapping`` to ``True`` in your config or pass the ``-e`` option into the command line utility. + +Custom Sections and Ordering +============================ + +You can change the section order with ``sections`` option from the default of: + +.. code-block:: ini + + FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER + +to your preference: + +.. code-block:: ini + + sections=FUTURE,STDLIB,FIRSTPARTY,THIRDPARTY,LOCALFOLDER + +You also can define your own sections and their order. + +Example: + +.. code-block:: ini + + known_django=django + known_pandas=pandas,numpy + sections=FUTURE,STDLIB,DJANGO,THIRDPARTY,PANDAS,FIRSTPARTY,LOCALFOLDER + +would create two new sections with the specified known modules. + +The ``no_lines_before`` option will prevent the listed sections from being split from the previous section by an empty line. + +Example: + +.. code-block:: ini + + sections=FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER + no_lines_before=LOCALFOLDER + +would produce a section with both FIRSTPARTY and LOCALFOLDER modules combined. + +Auto-comment import sections +============================ + +Some projects prefer to have import sections uniquely titled to aid in identifying the sections quickly +when visually scanning. isort can automate this as well. To do this simply set the ``import_heading_{section_name}`` +setting for each section you wish to have auto commented - to the desired comment. + +For Example: + +.. code-block:: ini + + import_heading_stdlib=Standard Library + import_heading_firstparty=My Stuff + +Would lead to output looking like the following: + +.. code-block:: python + + # Standard Library + import os + import sys + + import django.settings + + # My Stuff + import myproject.test + +Ordering by import length +========================= + +isort also makes it easy to sort your imports by length, simply by setting the ``length_sort`` option to ``True``. +This will result in the following output style: + +.. code-block:: python + + from evn.util import ( + Pool, + Dict, + Options, + Constant, + DecayDict, + UnexpectedCodePath, + ) + +It is also possible to opt-in to sorting imports by length for only specific +sections by using ``length_sort_`` followed by the section name as a +configuration item, e.g.:: + + length_sort_stdlib=1 + +Skip processing of imports (outside of configuration) +===================================================== + +To make isort ignore a single import simply add a comment at the end of the import line containing the text ``isort:skip``: + +.. code-block:: python + + import module # isort:skip + +or: + +.. code-block:: python + + from xyz import (abc, # isort:skip + yo, + hey) + +To make isort skip an entire file simply add ``isort:skip_file`` to the module's doc string: + +.. code-block:: python + + """ my_module.py + Best module ever + + isort:skip_file + """ + + import b + import a + +Adding an import to multiple files +================================== + +isort makes it easy to add an import statement across multiple files, while being assured it's correctly placed. + +From the command line: + +.. code-block:: bash + + isort -a "from __future__ import print_function" *.py + +from within Kate: + +.. code-block:: + + ctrl+] + +or: + +.. code-block:: + + menu > Python > Add Import + +Removing an import from multiple files +====================================== + +isort also makes it easy to remove an import from multiple files, without having to be concerned with how it was originally +formatted. + +From the command line: + +.. code-block:: bash + + isort -rm "os.system" *.py + +from within Kate: + +.. code-block:: + + ctrl+shift+] + +or: + +.. code-block:: + + menu > Python > Remove Import + +Using isort to verify code +========================== + +The ``--check-only`` option +--------------------------- + +isort can also be used to used to verify that code is correctly formatted by running it with ``-c``. +Any files that contain incorrectly sorted and/or formatted imports will be outputted to ``stderr``. + +.. code-block:: bash + + isort **/*.py -c -vb + + SUCCESS: /home/timothy/Projects/Open_Source/isort/isort_kate_plugin.py Everything Looks Good! + ERROR: /home/timothy/Projects/Open_Source/isort/isort/isort.py Imports are incorrectly sorted. + +One great place this can be used is with a pre-commit git hook, such as this one by @acdha: + +https://gist.github.com/acdha/8717683 + +This can help to ensure a certain level of code quality throughout a project. + + +Git hook +-------- + +isort provides a hook function that can be integrated into your Git pre-commit script to check +Python code before committing. + +To cause the commit to fail if there are isort errors (strict mode), include the following in +``.git/hooks/pre-commit``: + +.. code-block:: python + + #!/usr/bin/env python + import sys + from isort.hooks import git_hook + + sys.exit(git_hook(strict=True, modify=True)) + +If you just want to display warnings, but allow the commit to happen anyway, call ``git_hook`` without +the `strict` parameter. If you want to display warnings, but not also fix the code, call ``git_hook`` without +the `modify` parameter. + +Setuptools integration +---------------------- + +Upon installation, isort enables a ``setuptools`` command that checks Python files +declared by your project. + +Running ``python setup.py isort`` on the command line will check the files +listed in your ``py_modules`` and ``packages``. If any warning is found, +the command will exit with an error code: + +.. code-block:: bash + + $ python setup.py isort + +Also, to allow users to be able to use the command without having to install +isort themselves, add isort to the setup_requires of your ``setup()`` like so: + +.. code-block:: python + + setup( + name="project", + packages=["project"], + + setup_requires=[ + "isort" + ] + ) + + +Why isort? +========== + +isort simply stands for import sort. It was originally called "sortImports" however I got tired of typing the extra +characters and came to the realization camelCase is not pythonic. + +I wrote isort because in an organization I used to work in the manager came in one day and decided all code must +have alphabetically sorted imports. The code base was huge - and he meant for us to do it by hand. However, being a +programmer - I'm too lazy to spend 8 hours mindlessly performing a function, but not too lazy to spend 16 +hours automating it. I was given permission to open source sortImports and here we are :) + +-------------------------------------------- + +Thanks and I hope you find isort useful! + +~Timothy Crosley + + diff --git a/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/RECORD b/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/RECORD new file mode 100644 index 0000000..cb32637 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/RECORD @@ -0,0 +1,30 @@ +../../../bin/isort,sha256=to4mIYHfI0M3oL8YpRUCSJA0z0j1NgaYeq3PHNUs1Q0,269 +isort-4.3.21.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +isort-4.3.21.dist-info/LICENSE,sha256=BjKUABw9Uj26y6ud1UrCKZgnVsyvWSylMkCysM3YIGU,1089 +isort-4.3.21.dist-info/METADATA,sha256=8fY1DuLOn_UnCH58A8AcsCUZpYWeLCsQF-n-GIXlxOM,19749 +isort-4.3.21.dist-info/RECORD,, +isort-4.3.21.dist-info/WHEEL,sha256=h_aVn5OB2IERUjMbi2pucmR_zzWJtk303YXvhh60NJ8,110 +isort-4.3.21.dist-info/entry_points.txt,sha256=2M99eSFpnteDm3ekW8jya2a3A0-vFntKdT1fP93Tyks,148 +isort-4.3.21.dist-info/top_level.txt,sha256=mrBLoVpJnQWBbnMOSdzkjN1E9Z-e3Zd-MRlo88bstUk,6 +isort/__init__.py,sha256=_DTTMASePJCqsKnRJPf_YgFv5ZJOHg1uPLkrQHcqA2c,1380 +isort/__main__.py,sha256=9tThPqyOnY86bHaxJ0WciTd-rfKN3O-PQoNGBO2xhio,205 +isort/__pycache__/__init__.cpython-37.pyc,, +isort/__pycache__/__main__.cpython-37.pyc,, +isort/__pycache__/finders.cpython-37.pyc,, +isort/__pycache__/hooks.cpython-37.pyc,, +isort/__pycache__/isort.cpython-37.pyc,, +isort/__pycache__/main.cpython-37.pyc,, +isort/__pycache__/natural.cpython-37.pyc,, +isort/__pycache__/pie_slice.cpython-37.pyc,, +isort/__pycache__/pylama_isort.cpython-37.pyc,, +isort/__pycache__/settings.cpython-37.pyc,, +isort/__pycache__/utils.cpython-37.pyc,, +isort/finders.py,sha256=0w39ygCFuOv40OHixflrOv_Xna8K78usa5ySwS9GkWE,13185 +isort/hooks.py,sha256=GcyPMF7raq3B1z4ubbzIWoMiY5DePDni0Nct5U87uMQ,3230 +isort/isort.py,sha256=krLW0QgwnVjUD3hYTpQmWkMa5TDEZxx6AbX80vlVNoA,53910 +isort/main.py,sha256=rS7dAu_0T-8Jxi3sDWu00IOjt4j0r3vJi6bXZn6RnQg,21974 +isort/natural.py,sha256=hlcWsGKfIUC4Atjp5IIqDCmg1madY6ave9oNiTGjJ0Q,1794 +isort/pie_slice.py,sha256=hO6l1XocvGAd8XTR8526r-G7XIncUQB53_DHQ4AZEYI,5612 +isort/pylama_isort.py,sha256=wF6NOEVuibme0l-5pH9pCW1j4vGaFamuwll494TnzDI,785 +isort/settings.py,sha256=4_Jf-9GaBy9fi6UJctLqesIAMAegWekRIJdJmH5TBNE,17452 +isort/utils.py,sha256=KtazEoeX9XmtcrUGP6xl5lBX7Ye2N08ACGaWxiGcIaE,1344 diff --git a/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/WHEEL b/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/WHEEL new file mode 100644 index 0000000..78e6f69 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.33.4) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/entry_points.txt b/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/entry_points.txt new file mode 100644 index 0000000..3a77a18 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/entry_points.txt @@ -0,0 +1,9 @@ +[console_scripts] +isort = isort.main:main + +[distutils.commands] +isort = isort.main:ISortCommand + +[pylama.linter] +isort = isort.pylama_isort:Linter + diff --git a/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/top_level.txt b/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/top_level.txt new file mode 100644 index 0000000..2a79243 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort-4.3.21.dist-info/top_level.txt @@ -0,0 +1 @@ +isort diff --git a/Display/.venv/lib/python3.7/site-packages/isort/__init__.py b/Display/.venv/lib/python3.7/site-packages/isort/__init__.py new file mode 100644 index 0000000..9a0a073 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort/__init__.py @@ -0,0 +1,28 @@ +"""__init__.py. + +Defines the isort module to include the SortImports utility class as well as any defined settings. + +Copyright (C) 2013 Timothy Edmund Crosley + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and +to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +""" + +from __future__ import absolute_import, division, print_function, unicode_literals + +from . import settings # noqa: F401 +from .isort import SortImports # noqa: F401 + +__version__ = "4.3.21" diff --git a/Display/.venv/lib/python3.7/site-packages/isort/__main__.py b/Display/.venv/lib/python3.7/site-packages/isort/__main__.py new file mode 100644 index 0000000..91cc154 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort/__main__.py @@ -0,0 +1,9 @@ +from __future__ import absolute_import + +from isort.pie_slice import apply_changes_to_python_environment + +apply_changes_to_python_environment() + +from isort.main import main # noqa: E402 isort:skip + +main() diff --git a/Display/.venv/lib/python3.7/site-packages/isort/finders.py b/Display/.venv/lib/python3.7/site-packages/isort/finders.py new file mode 100644 index 0000000..225bd12 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort/finders.py @@ -0,0 +1,382 @@ +"""Finders try to find right section for passed module name +""" +from __future__ import absolute_import, division, print_function, unicode_literals + +import inspect +import os +import os.path +import re +import sys +import sysconfig +from fnmatch import fnmatch +from glob import glob + +from .pie_slice import PY2 +from .utils import chdir, exists_case_sensitive + +try: + from pipreqs import pipreqs +except ImportError: + pipreqs = None + +try: + from pip_api import parse_requirements +except ImportError: + parse_requirements = None + +try: + from requirementslib import Pipfile +except ImportError: + Pipfile = None + +try: + from functools import lru_cache +except ImportError: + from backports.functools_lru_cache import lru_cache + + +KNOWN_SECTION_MAPPING = { + 'STDLIB': 'STANDARD_LIBRARY', + 'FUTURE': 'FUTURE_LIBRARY', + 'FIRSTPARTY': 'FIRST_PARTY', + 'THIRDPARTY': 'THIRD_PARTY', +} + + +class BaseFinder(object): + def __init__(self, config, sections): + self.config = config + self.sections = sections + + +class ForcedSeparateFinder(BaseFinder): + def find(self, module_name): + for forced_separate in self.config['forced_separate']: + # Ensure all forced_separate patterns will match to end of string + path_glob = forced_separate + if not forced_separate.endswith('*'): + path_glob = '%s*' % forced_separate + + if fnmatch(module_name, path_glob) or fnmatch(module_name, '.' + path_glob): + return forced_separate + + +class LocalFinder(BaseFinder): + def find(self, module_name): + if module_name.startswith("."): + return self.sections.LOCALFOLDER + + +class KnownPatternFinder(BaseFinder): + def __init__(self, config, sections): + super(KnownPatternFinder, self).__init__(config, sections) + + self.known_patterns = [] + for placement in reversed(self.sections): + known_placement = KNOWN_SECTION_MAPPING.get(placement, placement) + config_key = 'known_{0}'.format(known_placement.lower()) + known_patterns = self.config.get(config_key, []) + known_patterns = [ + pattern + for known_pattern in known_patterns + for pattern in self._parse_known_pattern(known_pattern) + ] + for known_pattern in known_patterns: + regexp = '^' + known_pattern.replace('*', '.*').replace('?', '.?') + '$' + self.known_patterns.append((re.compile(regexp), placement)) + + @staticmethod + def _is_package(path): + """ + Evaluates if path is a python package + """ + if PY2: + return os.path.exists(os.path.join(path, '__init__.py')) + else: + return os.path.isdir(path) + + def _parse_known_pattern(self, pattern): + """ + Expand pattern if identified as a directory and return found sub packages + """ + if pattern.endswith(os.path.sep): + patterns = [ + filename + for filename in os.listdir(pattern) + if self._is_package(os.path.join(pattern, filename)) + ] + else: + patterns = [pattern] + + return patterns + + def find(self, module_name): + # Try to find most specific placement instruction match (if any) + parts = module_name.split('.') + module_names_to_check = ('.'.join(parts[:first_k]) for first_k in range(len(parts), 0, -1)) + for module_name_to_check in module_names_to_check: + for pattern, placement in self.known_patterns: + if pattern.match(module_name_to_check): + return placement + + +class PathFinder(BaseFinder): + def __init__(self, config, sections): + super(PathFinder, self).__init__(config, sections) + + # restore the original import path (i.e. not the path to bin/isort) + self.paths = [os.getcwd()] + + # virtual env + self.virtual_env = self.config.get('virtual_env') or os.environ.get('VIRTUAL_ENV') + if self.virtual_env: + self.virtual_env = os.path.realpath(self.virtual_env) + self.virtual_env_src = False + if self.virtual_env: + self.virtual_env_src = '{0}/src/'.format(self.virtual_env) + for path in glob('{0}/lib/python*/site-packages'.format(self.virtual_env)): + if path not in self.paths: + self.paths.append(path) + for path in glob('{0}/lib/python*/*/site-packages'.format(self.virtual_env)): + if path not in self.paths: + self.paths.append(path) + for path in glob('{0}/src/*'.format(self.virtual_env)): + if os.path.isdir(path): + self.paths.append(path) + + # conda + self.conda_env = self.config.get('conda_env') or os.environ.get('CONDA_PREFIX') + if self.conda_env: + self.conda_env = os.path.realpath(self.conda_env) + for path in glob('{0}/lib/python*/site-packages'.format(self.conda_env)): + if path not in self.paths: + self.paths.append(path) + for path in glob('{0}/lib/python*/*/site-packages'.format(self.conda_env)): + if path not in self.paths: + self.paths.append(path) + + # handle case-insensitive paths on windows + self.stdlib_lib_prefix = os.path.normcase(sysconfig.get_paths()['stdlib']) + if self.stdlib_lib_prefix not in self.paths: + self.paths.append(self.stdlib_lib_prefix) + + # handle compiled libraries + self.ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") or ".so" + + # add system paths + for path in sys.path[1:]: + if path not in self.paths: + self.paths.append(path) + + def find(self, module_name): + for prefix in self.paths: + package_path = "/".join((prefix, module_name.split(".")[0])) + is_module = (exists_case_sensitive(package_path + ".py") or + exists_case_sensitive(package_path + ".so") or + exists_case_sensitive(package_path + self.ext_suffix) or + exists_case_sensitive(package_path + "/__init__.py")) + is_package = exists_case_sensitive(package_path) and os.path.isdir(package_path) + if is_module or is_package: + if 'site-packages' in prefix: + return self.sections.THIRDPARTY + if 'dist-packages' in prefix: + return self.sections.THIRDPARTY + if self.virtual_env and self.virtual_env_src in prefix: + return self.sections.THIRDPARTY + if self.conda_env and self.conda_env in prefix: + return self.sections.THIRDPARTY + if os.path.normcase(prefix).startswith(self.stdlib_lib_prefix): + return self.sections.STDLIB + return self.config['default_section'] + + +class ReqsBaseFinder(BaseFinder): + def __init__(self, config, sections, path='.'): + super(ReqsBaseFinder, self).__init__(config, sections) + self.path = path + if self.enabled: + self.mapping = self._load_mapping() + self.names = self._load_names() + + @staticmethod + def _load_mapping(): + """Return list of mappings `package_name -> module_name` + + Example: + django-haystack -> haystack + """ + if not pipreqs: + return + path = os.path.dirname(inspect.getfile(pipreqs)) + path = os.path.join(path, 'mapping') + with open(path) as f: + # pypi_name: import_name + return dict(line.strip().split(":")[::-1] for line in f) + + def _load_names(self): + """Return list of thirdparty modules from requirements + """ + names = [] + for path in self._get_files(): + for name in self._get_names(path): + names.append(self._normalize_name(name)) + return names + + @staticmethod + def _get_parents(path): + prev = '' + while path != prev: + prev = path + yield path + path = os.path.dirname(path) + + def _get_files(self): + """Return paths to all requirements files + """ + path = os.path.abspath(self.path) + if os.path.isfile(path): + path = os.path.dirname(path) + + for path in self._get_parents(path): + for file_path in self._get_files_from_dir(path): + yield file_path + + def _normalize_name(self, name): + """Convert package name to module name + + Examples: + Django -> django + django-haystack -> haystack + Flask-RESTFul -> flask_restful + """ + if self.mapping: + name = self.mapping.get(name, name) + return name.lower().replace('-', '_') + + def find(self, module_name): + # required lib not installed yet + if not self.enabled: + return + + module_name, _sep, _submodules = module_name.partition('.') + module_name = module_name.lower() + if not module_name: + return + + for name in self.names: + if module_name == name: + return self.sections.THIRDPARTY + + +class RequirementsFinder(ReqsBaseFinder): + exts = ('.txt', '.in') + enabled = bool(parse_requirements) + + def _get_files_from_dir(self, path): + """Return paths to requirements files from passed dir. + """ + return RequirementsFinder._get_files_from_dir_cached(path) + + @classmethod + @lru_cache(maxsize=16) + def _get_files_from_dir_cached(cls, path): + result = [] + + for fname in os.listdir(path): + if 'requirements' not in fname: + continue + full_path = os.path.join(path, fname) + + # *requirements*/*.{txt,in} + if os.path.isdir(full_path): + for subfile_name in os.listdir(path): + for ext in cls.exts: + if subfile_name.endswith(ext): + result.append(os.path.join(path, subfile_name)) + continue + + # *requirements*.{txt,in} + if os.path.isfile(full_path): + for ext in cls.exts: + if fname.endswith(ext): + result.append(full_path) + break + + return result + + def _get_names(self, path): + """Load required packages from path to requirements file + """ + return RequirementsFinder._get_names_cached(path) + + @classmethod + @lru_cache(maxsize=16) + def _get_names_cached(cls, path): + results = [] + + with chdir(os.path.dirname(path)): + requirements = parse_requirements(path) + for req in requirements.values(): + if req.name: + results.append(req.name) + + return results + + +class PipfileFinder(ReqsBaseFinder): + enabled = bool(Pipfile) + + def _get_names(self, path): + with chdir(path): + project = Pipfile.load(path) + for req in project.packages: + yield req.name + + def _get_files_from_dir(self, path): + if 'Pipfile' in os.listdir(path): + yield path + + +class DefaultFinder(BaseFinder): + def find(self, module_name): + return self.config['default_section'] + + +class FindersManager(object): + finders = ( + ForcedSeparateFinder, + LocalFinder, + KnownPatternFinder, + PathFinder, + PipfileFinder, + RequirementsFinder, + DefaultFinder, + ) + + def __init__(self, config, sections, finders=None): + self.verbose = config.get('verbose', False) + + finders = self.finders if finders is None else finders + self.finders = [] + for finder in finders: + try: + self.finders.append(finder(config, sections)) + except Exception as exception: + # if one finder fails to instantiate isort can continue using the rest + if self.verbose: + print('{} encountered an error ({}) during instantiation and cannot be used'.format(finder.__name__, + str(exception))) + self.finders = tuple(self.finders) + + def find(self, module_name): + for finder in self.finders: + try: + section = finder.find(module_name) + except Exception as exception: + # isort has to be able to keep trying to identify the correct import section even if one approach fails + if self.verbose: + print('{} encountered an error ({}) while trying to identify the {} module'.format(finder.__name__, + str(exception), + module_name)) + if section is not None: + return section diff --git a/Display/.venv/lib/python3.7/site-packages/isort/hooks.py b/Display/.venv/lib/python3.7/site-packages/isort/hooks.py new file mode 100644 index 0000000..16a16e1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort/hooks.py @@ -0,0 +1,91 @@ +"""isort.py. + +Defines a git hook to allow pre-commit warnings and errors about import order. + +usage: + exit_code = git_hook(strict=True|False, modify=True|False) + +Copyright (C) 2015 Helen Sherwood-Taylor + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and +to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +""" +import subprocess + +from isort import SortImports + + +def get_output(command): + """ + Run a command and return raw output + + :param str command: the command to run + :returns: the stdout output of the command + """ + return subprocess.check_output(command.split()) + + +def get_lines(command): + """ + Run a command and return lines of output + + :param str command: the command to run + :returns: list of whitespace-stripped lines output by command + """ + stdout = get_output(command) + return [line.strip().decode('utf-8') for line in stdout.splitlines()] + + +def git_hook(strict=False, modify=False): + """ + Git pre-commit hook to check staged files for isort errors + + :param bool strict - if True, return number of errors on exit, + causing the hook to fail. If False, return zero so it will + just act as a warning. + :param bool modify - if True, fix the sources if they are not + sorted properly. If False, only report result without + modifying anything. + + :return number of errors if in strict mode, 0 otherwise. + """ + + # Get list of files modified and staged + diff_cmd = "git diff-index --cached --name-only --diff-filter=ACMRTUXB HEAD" + files_modified = get_lines(diff_cmd) + + errors = 0 + for filename in files_modified: + if filename.endswith('.py'): + # Get the staged contents of the file + staged_cmd = "git show :%s" % filename + staged_contents = get_output(staged_cmd) + + sort = SortImports( + file_path=filename, + file_contents=staged_contents.decode(), + check=True + ) + + if sort.incorrectly_sorted: + errors += 1 + if modify: + SortImports( + file_path=filename, + file_contents=staged_contents.decode(), + check=False, + ) + + return errors if strict else 0 diff --git a/Display/.venv/lib/python3.7/site-packages/isort/isort.py b/Display/.venv/lib/python3.7/site-packages/isort/isort.py new file mode 100644 index 0000000..245e53f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort/isort.py @@ -0,0 +1,1060 @@ +"""isort.py. + +Exposes a simple library to sort through imports within Python code + +usage: + SortImports(file_name) +or: + sorted = SortImports(file_contents=file_contents).output + +Copyright (C) 2013 Timothy Edmund Crosley + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and +to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +""" +from __future__ import absolute_import, division, print_function, unicode_literals + +import codecs +import copy +import io +import itertools +import os +import re +import sys +from collections import OrderedDict, namedtuple +from datetime import datetime +from difflib import unified_diff + +from . import settings +from .finders import FindersManager +from .natural import nsorted +from .pie_slice import input + + +class SortImports(object): + incorrectly_sorted = False + skipped = False + + def __init__(self, file_path=None, file_contents=None, file_=None, write_to_stdout=False, check=False, + show_diff=False, settings_path=None, ask_to_apply=False, run_path='', check_skip=True, + extension=None, **setting_overrides): + if not settings_path and file_path: + settings_path = os.path.dirname(os.path.abspath(file_path)) + settings_path = settings_path or os.getcwd() + + self.config = settings.from_path(settings_path).copy() + for key, value in setting_overrides.items(): + access_key = key.replace('not_', '').lower() + # The sections config needs to retain order and can't be converted to a set. + if access_key != 'sections' and type(self.config.get(access_key)) in (list, tuple): + if key.startswith('not_'): + self.config[access_key] = list(set(self.config[access_key]).difference(value)) + else: + self.config[access_key] = list(set(self.config[access_key]).union(value)) + else: + self.config[key] = value + + if self.config['force_alphabetical_sort']: + self.config.update({'force_alphabetical_sort_within_sections': True, + 'no_sections': True, + 'lines_between_types': 1, + 'from_first': True}) + + indent = str(self.config['indent']) + if indent.isdigit(): + indent = " " * int(indent) + else: + indent = indent.strip("'").strip('"') + if indent.lower() == "tab": + indent = "\t" + self.config['indent'] = indent + + self.config['comment_prefix'] = self.config['comment_prefix'].strip("'").strip('"') + + self.place_imports = {} + self.import_placements = {} + self.remove_imports = [self._format_simplified(removal) for removal in self.config['remove_imports']] + self.add_imports = [self._format_natural(addition) for addition in self.config['add_imports']] + self._section_comments = ["# " + value for key, value in self.config.items() if + key.startswith('import_heading') and value] + + self.file_encoding = 'utf-8' + file_name = file_path + self.file_path = file_path or "" + if file_path: + file_path = os.path.abspath(file_path) + if check_skip: + if run_path and file_path.startswith(run_path): + file_name = file_path.replace(run_path, '', 1) + else: + file_name = file_path + run_path = '' + + if settings.should_skip(file_name, self.config, run_path): + self.skipped = True + if self.config['verbose']: + print("WARNING: {0} was skipped as it's listed in 'skip' setting" + " or matches a glob in 'skip_glob' setting".format(file_path)) + file_contents = None + if not self.skipped and not file_contents: + with io.open(file_path, 'rb') as f: + file_encoding = coding_check(f) + with io.open(file_path, encoding=file_encoding, newline='') as file_to_import_sort: + try: + file_contents = file_to_import_sort.read() + self.file_path = file_path + self.file_encoding = file_encoding + encoding_success = True + except UnicodeDecodeError: + encoding_success = False + + if not encoding_success: + with io.open(file_path, newline='') as file_to_import_sort: + try: + file_contents = file_to_import_sort.read() + self.file_path = file_path + self.file_encoding = file_to_import_sort.encoding + except UnicodeDecodeError: + encoding_success = False + file_contents = None + self.skipped = True + if self.config['verbose']: + print("WARNING: {} was skipped as it couldn't be opened with the given " + "{} encoding or {} fallback encoding".format(file_path, + self.file_encoding, + file_to_import_sort.encoding)) + elif file_: + try: + file_.seek(0) + self.file_encoding = coding_check(file_) + file_.seek(0) + except (io.UnsupportedOperation, IOError): + pass + reader = codecs.getreader(self.file_encoding) + file_contents = reader(file_).read() + + # try to decode file_contents + if file_contents: + try: + basestring + # python 2 + need_decode = (str, bytes) + except NameError: + # python 3 + need_decode = bytes + + if isinstance(file_contents, need_decode): + file_contents = file_contents.decode(coding_check(file_contents.splitlines())) + + if file_contents is None or ("isort:" + "skip_file") in file_contents: + self.skipped = True + self.output = None + if write_to_stdout and file_contents: + sys.stdout.write(file_contents) + return + + if self.config['line_ending']: + self.line_separator = self.config['line_ending'] + else: + if '\r\n' in file_contents: + self.line_separator = '\r\n' + elif '\r' in file_contents: + self.line_separator = '\r' + else: + self.line_separator = '\n' + self.in_lines = file_contents.split(self.line_separator) + self.original_length = len(self.in_lines) + if (self.original_length > 1 or self.in_lines[:1] not in ([], [""])) or self.config['force_adds']: + for add_import in self.add_imports: + self.in_lines.append(add_import) + self.number_of_lines = len(self.in_lines) + + if not extension: + self.extension = file_name.split('.')[-1] if file_name else "py" + else: + self.extension = extension + + self.out_lines = [] + self.comments = {'from': {}, 'straight': {}, 'nested': {}, 'above': {'straight': {}, 'from': {}}} + self.imports = OrderedDict() + self.as_map = {} + + section_names = self.config['sections'] + self.sections = namedtuple('Sections', section_names)(*[name for name in section_names]) + for section in itertools.chain(self.sections, self.config['forced_separate']): + self.imports[section] = {'straight': OrderedDict(), 'from': OrderedDict()} + + self.finder = FindersManager(config=self.config, sections=self.sections) + + self.index = 0 + self.import_index = -1 + self._first_comment_index_start = -1 + self._first_comment_index_end = -1 + self._parse() + if self.import_index != -1: + self._add_formatted_imports() + self.length_change = len(self.out_lines) - self.original_length + while self.out_lines and self.out_lines[-1].strip() == "": + self.out_lines.pop(-1) + self.out_lines.append("") + self.output = self.line_separator.join(self.out_lines) + if self.config['atomic']: + try: + compile(self._strip_top_comments(self.out_lines, self.line_separator), self.file_path, 'exec', 0, 1) + except SyntaxError: + self.output = file_contents + self.incorrectly_sorted = True + try: + compile(self._strip_top_comments(self.in_lines, self.line_separator), self.file_path, 'exec', 0, 1) + print("ERROR: {0} isort would have introduced syntax errors, please report to the project!". + format(self.file_path)) + except SyntaxError: + print("ERROR: {0} File contains syntax errors.".format(self.file_path)) + + return + if check: + check_output = self.output + check_against = file_contents + if self.config['ignore_whitespace']: + check_output = check_output.replace(self.line_separator, "").replace(" ", "").replace("\x0c", "") + check_against = check_against.replace(self.line_separator, "").replace(" ", "").replace("\x0c", "") + + if check_output.strip() == check_against.strip(): + if self.config['verbose']: + print("SUCCESS: {0} Everything Looks Good!".format(self.file_path)) + return + + print("ERROR: {0} Imports are incorrectly sorted.".format(self.file_path)) + self.incorrectly_sorted = True + if show_diff or self.config['show_diff']: + self._show_diff(file_contents) + elif write_to_stdout: + if sys.version_info[0] < 3: + self.output = self.output.encode(self.file_encoding) + sys.stdout.write(self.output) + elif file_name and not check: + if self.output == file_contents: + return + + if ask_to_apply: + self._show_diff(file_contents) + answer = None + while answer not in ('yes', 'y', 'no', 'n', 'quit', 'q'): + answer = input("Apply suggested changes to '{0}' [y/n/q]? ".format(self.file_path)).lower() + if answer in ('no', 'n'): + return + if answer in ('quit', 'q'): + sys.exit(1) + with io.open(self.file_path, encoding=self.file_encoding, mode='w', newline='') as output_file: + if not self.config['quiet']: + print("Fixing {0}".format(self.file_path)) + output_file.write(self.output) + + @property + def correctly_sorted(self): + return not self.incorrectly_sorted + + def _show_diff(self, file_contents): + for line in unified_diff( + file_contents.splitlines(1), + self.output.splitlines(1), + fromfile=self.file_path + ':before', + tofile=self.file_path + ':after', + fromfiledate=str(datetime.fromtimestamp(os.path.getmtime(self.file_path)) + if self.file_path else datetime.now()), + tofiledate=str(datetime.now()) + ): + sys.stdout.write(line) + + @staticmethod + def _strip_top_comments(lines, line_separator): + """Strips # comments that exist at the top of the given lines""" + lines = copy.copy(lines) + while lines and lines[0].startswith("#"): + lines = lines[1:] + return line_separator.join(lines) + + def place_module(self, module_name): + """Tries to determine if a module is a python std import, third party import, or project code: + + if it can't determine - it assumes it is project code + + """ + return self.finder.find(module_name) + + def _get_line(self): + """Returns the current line from the file while incrementing the index.""" + line = self.in_lines[self.index] + self.index += 1 + return line + + @staticmethod + def _import_type(line): + """If the current line is an import line it will return its type (from or straight)""" + if "isort:skip" in line: + return + elif line.startswith('import '): + return "straight" + elif line.startswith('from '): + return "from" + + def _at_end(self): + """returns True if we are at the end of the file.""" + return self.index == self.number_of_lines + + @staticmethod + def _module_key(module_name, config, sub_imports=False, ignore_case=False, section_name=None): + match = re.match(r'^(\.+)\s*(.*)', module_name) + if match: + sep = ' ' if config['reverse_relative'] else '_' + module_name = sep.join(match.groups()) + + prefix = "" + if ignore_case: + module_name = str(module_name).lower() + else: + module_name = str(module_name) + + if sub_imports and config['order_by_type']: + if module_name.isupper() and len(module_name) > 1: + prefix = "A" + elif module_name[0:1].isupper(): + prefix = "B" + else: + prefix = "C" + if not config['case_sensitive']: + module_name = module_name.lower() + if section_name is None or 'length_sort_' + str(section_name).lower() not in config: + length_sort = config['length_sort'] + else: + length_sort = config['length_sort_' + str(section_name).lower()] + return "{0}{1}{2}".format(module_name in config['force_to_top'] and "A" or "B", prefix, + length_sort and (str(len(module_name)) + ":" + module_name) or module_name) + + def _add_comments(self, comments, original_string=""): + """ + Returns a string with comments added if ignore_comments is not set. + """ + + if not self.config['ignore_comments']: + return comments and "{0}{1} {2}".format(self._strip_comments(original_string)[0], + self.config['comment_prefix'], + "; ".join(comments)) or original_string + + return comments and self._strip_comments(original_string)[0] + + def _wrap(self, line): + """ + Returns an import wrapped to the specified line-length, if possible. + """ + wrap_mode = self.config['multi_line_output'] + if len(line) > self.config['line_length'] and wrap_mode != settings.WrapModes.NOQA: + line_without_comment = line + comment = None + if '#' in line: + line_without_comment, comment = line.split('#', 1) + for splitter in ("import ", ".", "as "): + exp = r"\b" + re.escape(splitter) + r"\b" + if re.search(exp, line_without_comment) and not line_without_comment.strip().startswith(splitter): + line_parts = re.split(exp, line_without_comment) + if comment: + line_parts[-1] = '{0}#{1}'.format(line_parts[-1], comment) + next_line = [] + while (len(line) + 2) > (self.config['wrap_length'] or self.config['line_length']) and line_parts: + next_line.append(line_parts.pop()) + line = splitter.join(line_parts) + if not line: + line = next_line.pop() + + cont_line = self._wrap(self.config['indent'] + splitter.join(next_line).lstrip()) + if self.config['use_parentheses']: + if splitter == "as ": + output = "{0}{1}{2}".format(line, splitter, cont_line.lstrip()) + else: + output = "{0}{1}({2}{3}{4}{5})".format( + line, splitter, self.line_separator, cont_line, + "," if self.config['include_trailing_comma'] else "", + self.line_separator if wrap_mode in (settings.WrapModes.VERTICAL_HANGING_INDENT, + settings.WrapModes.VERTICAL_GRID_GROUPED) + else "") + lines = output.split(self.line_separator) + if self.config['comment_prefix'] in lines[-1] and lines[-1].endswith(')'): + line, comment = lines[-1].split(self.config['comment_prefix'], 1) + lines[-1] = line + ')' + self.config['comment_prefix'] + comment[:-1] + return self.line_separator.join(lines) + return "{0}{1}\\{2}{3}".format(line, splitter, self.line_separator, cont_line) + elif len(line) > self.config['line_length'] and wrap_mode == settings.WrapModes.NOQA: + if "# NOQA" not in line: + return "{0}{1} NOQA".format(line, self.config['comment_prefix']) + + return line + + def _add_straight_imports(self, straight_modules, section, section_output): + for module in straight_modules: + if module in self.remove_imports: + continue + + if module in self.as_map: + import_definition = '' + if self.config['keep_direct_and_as_imports']: + import_definition = "import {0}\n".format(module) + import_definition += "import {0} as {1}".format(module, self.as_map[module]) + else: + import_definition = "import {0}".format(module) + + comments_above = self.comments['above']['straight'].pop(module, None) + if comments_above: + section_output.extend(comments_above) + section_output.append(self._add_comments(self.comments['straight'].get(module), import_definition)) + + def _add_from_imports(self, from_modules, section, section_output, ignore_case): + for module in from_modules: + if module in self.remove_imports: + continue + + import_start = "from {0} import ".format(module) + from_imports = list(self.imports[section]['from'][module]) + if not self.config['no_inline_sort'] or self.config['force_single_line']: + from_imports = nsorted(from_imports, key=lambda key: self._module_key(key, self.config, True, ignore_case, section_name=section)) + if self.remove_imports: + from_imports = [line for line in from_imports if not "{0}.{1}".format(module, line) in + self.remove_imports] + + sub_modules = ['{0}.{1}'.format(module, from_import) for from_import in from_imports] + as_imports = { + from_import: "{0} as {1}".format(from_import, self.as_map[sub_module]) + for from_import, sub_module in zip(from_imports, sub_modules) + if sub_module in self.as_map + } + if self.config['combine_as_imports'] and not ("*" in from_imports and self.config['combine_star']): + for from_import in copy.copy(from_imports): + if from_import in as_imports: + from_imports[from_imports.index(from_import)] = as_imports.pop(from_import) + + while from_imports: + comments = self.comments['from'].pop(module, ()) + if "*" in from_imports and self.config['combine_star']: + import_statement = self._wrap(self._add_comments(comments, "{0}*".format(import_start))) + from_imports = None + elif self.config['force_single_line']: + import_statements = [] + while from_imports: + from_import = from_imports.pop(0) + if from_import in as_imports: + from_comments = self.comments['straight'].get('{}.{}'.format(module, from_import)) + import_statements.append(self._add_comments(from_comments, + self._wrap(import_start + as_imports[from_import]))) + continue + single_import_line = self._add_comments(comments, import_start + from_import) + comment = self.comments['nested'].get(module, {}).pop(from_import, None) + if comment: + single_import_line += "{0} {1}".format(comments and ";" or self.config['comment_prefix'], + comment) + import_statements.append(self._wrap(single_import_line)) + comments = None + import_statement = self.line_separator.join(import_statements) + else: + while from_imports and from_imports[0] in as_imports: + from_import = from_imports.pop(0) + from_comments = self.comments['straight'].get('{}.{}'.format(module, from_import)) + above_comments = self.comments['above']['from'].pop(module, None) + if above_comments: + section_output.extend(above_comments) + + section_output.append(self._add_comments(from_comments, + self._wrap(import_start + as_imports[from_import]))) + + star_import = False + if "*" in from_imports: + section_output.append(self._add_comments(comments, "{0}*".format(import_start))) + from_imports.remove('*') + star_import = True + comments = None + + for from_import in copy.copy(from_imports): + if from_import in as_imports: + continue + comment = self.comments['nested'].get(module, {}).pop(from_import, None) + if comment: + single_import_line = self._add_comments(comments, import_start + from_import) + single_import_line += "{0} {1}".format(comments and ";" or self.config['comment_prefix'], + comment) + above_comments = self.comments['above']['from'].pop(module, None) + if above_comments: + section_output.extend(above_comments) + section_output.append(self._wrap(single_import_line)) + from_imports.remove(from_import) + comments = None + + from_import_section = [] + while from_imports and from_imports[0] not in as_imports: + from_import_section.append(from_imports.pop(0)) + if star_import: + import_statement = import_start + (", ").join(from_import_section) + else: + import_statement = self._add_comments(comments, import_start + (", ").join(from_import_section)) + if not from_import_section: + import_statement = "" + + do_multiline_reformat = False + + force_grid_wrap = self.config['force_grid_wrap'] + if force_grid_wrap and len(from_import_section) >= force_grid_wrap: + do_multiline_reformat = True + + if len(import_statement) > self.config['line_length'] and len(from_import_section) > 1: + do_multiline_reformat = True + + # If line too long AND have imports AND we are NOT using GRID or VERTICAL wrap modes + if (len(import_statement) > self.config['line_length'] and len(from_import_section) > 0 and + self.config['multi_line_output'] not in (1, 0)): + do_multiline_reformat = True + + if do_multiline_reformat: + import_statement = self._multi_line_reformat(import_start, from_import_section, comments) + if self.config['multi_line_output'] == 0: + self.config['multi_line_output'] = 4 + try: + other_import_statement = self._multi_line_reformat(import_start, from_import_section, comments) + if (max(len(x) + for x in import_statement.split('\n')) > self.config['line_length']): + import_statement = other_import_statement + finally: + self.config['multi_line_output'] = 0 + if not do_multiline_reformat and len(import_statement) > self.config['line_length']: + import_statement = self._wrap(import_statement) + + if import_statement: + above_comments = self.comments['above']['from'].pop(module, None) + if above_comments: + section_output.extend(above_comments) + section_output.append(import_statement) + + def _multi_line_reformat(self, import_start, from_imports, comments): + output_mode = settings.WrapModes._fields[self.config['multi_line_output']].lower() + formatter = getattr(self, "_output_" + output_mode, self._output_grid) + dynamic_indent = " " * (len(import_start) + 1) + indent = self.config['indent'] + line_length = self.config['wrap_length'] or self.config['line_length'] + import_statement = formatter(import_start, copy.copy(from_imports), + dynamic_indent, indent, line_length, comments) + if self.config['balanced_wrapping']: + lines = import_statement.split(self.line_separator) + line_count = len(lines) + if len(lines) > 1: + minimum_length = min(len(line) for line in lines[:-1]) + else: + minimum_length = 0 + new_import_statement = import_statement + while (len(lines[-1]) < minimum_length and + len(lines) == line_count and line_length > 10): + import_statement = new_import_statement + line_length -= 1 + new_import_statement = formatter(import_start, copy.copy(from_imports), + dynamic_indent, indent, line_length, comments) + lines = new_import_statement.split(self.line_separator) + if import_statement.count(self.line_separator) == 0: + return self._wrap(import_statement) + return import_statement + + def _add_formatted_imports(self): + """Adds the imports back to the file. + + (at the index of the first import) sorted alphabetically and split between groups + + """ + sort_ignore_case = self.config['force_alphabetical_sort_within_sections'] + sections = itertools.chain(self.sections, self.config['forced_separate']) + + if self.config['no_sections']: + self.imports['no_sections'] = {'straight': [], 'from': {}} + for section in sections: + self.imports['no_sections']['straight'].extend(self.imports[section].get('straight', [])) + self.imports['no_sections']['from'].update(self.imports[section].get('from', {})) + sections = ('no_sections', ) + + output = [] + pending_lines_before = False + for section in sections: + straight_modules = self.imports[section]['straight'] + straight_modules = nsorted(straight_modules, key=lambda key: self._module_key(key, self.config, section_name=section)) + from_modules = self.imports[section]['from'] + from_modules = nsorted(from_modules, key=lambda key: self._module_key(key, self.config, section_name=section)) + + section_output = [] + if self.config['from_first']: + self._add_from_imports(from_modules, section, section_output, sort_ignore_case) + if self.config['lines_between_types'] and from_modules and straight_modules: + section_output.extend([''] * self.config['lines_between_types']) + self._add_straight_imports(straight_modules, section, section_output) + else: + self._add_straight_imports(straight_modules, section, section_output) + if self.config['lines_between_types'] and from_modules and straight_modules: + section_output.extend([''] * self.config['lines_between_types']) + self._add_from_imports(from_modules, section, section_output, sort_ignore_case) + + if self.config['force_sort_within_sections']: + def by_module(line): + section = 'B' + if line.startswith('#'): + return 'AA' + + line = re.sub('^from ', '', line) + line = re.sub('^import ', '', line) + if line.split(' ')[0] in self.config['force_to_top']: + section = 'A' + if not self.config['order_by_type']: + line = line.lower() + return '{0}{1}'.format(section, line) + section_output = nsorted(section_output, key=by_module) + + section_name = section + no_lines_before = section_name in self.config['no_lines_before'] + + if section_output: + if section_name in self.place_imports: + self.place_imports[section_name] = section_output + continue + + section_title = self.config.get('import_heading_' + str(section_name).lower(), '') + if section_title: + section_comment = "# {0}".format(section_title) + if section_comment not in self.out_lines[0:1] and section_comment not in self.in_lines[0:1]: + section_output.insert(0, section_comment) + + if pending_lines_before or not no_lines_before: + output += ([''] * self.config['lines_between_sections']) + + output += section_output + + pending_lines_before = False + else: + pending_lines_before = pending_lines_before or not no_lines_before + + while output and output[-1].strip() == '': + output.pop() + while output and output[0].strip() == '': + output.pop(0) + + output_at = 0 + if self.import_index < self.original_length: + output_at = self.import_index + elif self._first_comment_index_end != -1 and self._first_comment_index_start <= 2: + output_at = self._first_comment_index_end + self.out_lines[output_at:0] = output + + imports_tail = output_at + len(output) + while [character.strip() for character in self.out_lines[imports_tail: imports_tail + 1]] == [""]: + self.out_lines.pop(imports_tail) + + if len(self.out_lines) > imports_tail: + next_construct = "" + self._in_quote = False + tail = self.out_lines[imports_tail:] + + for index, line in enumerate(tail): + in_quote = self._in_quote + if not self._skip_line(line) and line.strip(): + if line.strip().startswith("#") and len(tail) > (index + 1) and tail[index + 1].strip(): + continue + next_construct = line + break + elif not in_quote: + parts = line.split() + if len(parts) >= 3 and parts[1] == '=' and "'" not in parts[0] and '"' not in parts[0]: + next_construct = line + break + + if self.config['lines_after_imports'] != -1: + self.out_lines[imports_tail:0] = ["" for line in range(self.config['lines_after_imports'])] + elif self.extension != "pyi" and (next_construct.startswith("def ") or + next_construct.startswith("class ") or + next_construct.startswith("@") or + next_construct.startswith("async def")): + self.out_lines[imports_tail:0] = ["", ""] + else: + self.out_lines[imports_tail:0] = [""] + + if self.place_imports: + new_out_lines = [] + for index, line in enumerate(self.out_lines): + new_out_lines.append(line) + if line in self.import_placements: + new_out_lines.extend(self.place_imports[self.import_placements[line]]) + if len(self.out_lines) <= index or self.out_lines[index + 1].strip() != "": + new_out_lines.append("") + self.out_lines = new_out_lines + + def _output_grid(self, statement, imports, white_space, indent, line_length, comments): + statement += "(" + imports.pop(0) + while imports: + next_import = imports.pop(0) + next_statement = self._add_comments(comments, statement + ", " + next_import) + if len(next_statement.split(self.line_separator)[-1]) + 1 > line_length: + lines = ['{0}{1}'.format(white_space, next_import.split(" ")[0])] + for part in next_import.split(" ")[1:]: + new_line = '{0} {1}'.format(lines[-1], part) + if len(new_line) + 1 > line_length: + lines.append('{0}{1}'.format(white_space, part)) + else: + lines[-1] = new_line + next_import = self.line_separator.join(lines) + statement = (self._add_comments(comments, "{0},".format(statement)) + + "{0}{1}".format(self.line_separator, next_import)) + comments = None + else: + statement += ", " + next_import + return statement + ("," if self.config['include_trailing_comma'] else "") + ")" + + def _output_vertical(self, statement, imports, white_space, indent, line_length, comments): + first_import = self._add_comments(comments, imports.pop(0) + ",") + self.line_separator + white_space + return "{0}({1}{2}{3})".format( + statement, + first_import, + ("," + self.line_separator + white_space).join(imports), + "," if self.config['include_trailing_comma'] else "", + ) + + def _output_hanging_indent(self, statement, imports, white_space, indent, line_length, comments): + statement += imports.pop(0) + while imports: + next_import = imports.pop(0) + next_statement = self._add_comments(comments, statement + ", " + next_import) + if len(next_statement.split(self.line_separator)[-1]) + 3 > line_length: + next_statement = (self._add_comments(comments, "{0}, \\".format(statement)) + + "{0}{1}{2}".format(self.line_separator, indent, next_import)) + comments = None + statement = next_statement + return statement + + def _output_vertical_hanging_indent(self, statement, imports, white_space, indent, line_length, comments): + return "{0}({1}{2}{3}{4}{5}{2})".format( + statement, + self._add_comments(comments), + self.line_separator, + indent, + ("," + self.line_separator + indent).join(imports), + "," if self.config['include_trailing_comma'] else "", + ) + + def _output_vertical_grid_common(self, statement, imports, white_space, indent, line_length, comments, + need_trailing_char): + statement += self._add_comments(comments, "(") + self.line_separator + indent + imports.pop(0) + while imports: + next_import = imports.pop(0) + next_statement = "{0}, {1}".format(statement, next_import) + current_line_length = len(next_statement.split(self.line_separator)[-1]) + if imports or need_trailing_char: + # If we have more imports we need to account for a comma after this import + # We might also need to account for a closing ) we're going to add. + current_line_length += 1 + if current_line_length > line_length: + next_statement = "{0},{1}{2}{3}".format(statement, self.line_separator, indent, next_import) + statement = next_statement + if self.config['include_trailing_comma']: + statement += ',' + return statement + + def _output_vertical_grid(self, statement, imports, white_space, indent, line_length, comments): + return self._output_vertical_grid_common(statement, imports, white_space, indent, line_length, comments, + True) + ")" + + def _output_vertical_grid_grouped(self, statement, imports, white_space, indent, line_length, comments): + return self._output_vertical_grid_common(statement, imports, white_space, indent, line_length, comments, + True) + self.line_separator + ")" + + def _output_vertical_grid_grouped_no_comma(self, statement, imports, white_space, indent, line_length, comments): + return self._output_vertical_grid_common(statement, imports, white_space, indent, line_length, comments, + False) + self.line_separator + ")" + + def _output_noqa(self, statement, imports, white_space, indent, line_length, comments): + retval = '{0}{1}'.format(statement, ', '.join(imports)) + comment_str = ' '.join(comments) + if comments: + if len(retval) + len(self.config['comment_prefix']) + 1 + len(comment_str) <= line_length: + return '{0}{1} {2}'.format(retval, self.config['comment_prefix'], comment_str) + else: + if len(retval) <= line_length: + return retval + if comments: + if "NOQA" in comments: + return '{0}{1} {2}'.format(retval, self.config['comment_prefix'], comment_str) + else: + return '{0}{1} NOQA {2}'.format(retval, self.config['comment_prefix'], comment_str) + else: + return '{0}{1} NOQA'.format(retval, self.config['comment_prefix']) + + @staticmethod + def _strip_comments(line, comments=None): + """Removes comments from import line.""" + if comments is None: + comments = [] + + new_comments = False + comment_start = line.find("#") + if comment_start != -1: + comments.append(line[comment_start + 1:].strip()) + new_comments = True + line = line[:comment_start] + + return line, comments, new_comments + + @staticmethod + def _format_simplified(import_line): + import_line = import_line.strip() + if import_line.startswith("from "): + import_line = import_line.replace("from ", "") + import_line = import_line.replace(" import ", ".") + elif import_line.startswith("import "): + import_line = import_line.replace("import ", "") + + return import_line + + @staticmethod + def _format_natural(import_line): + import_line = import_line.strip() + if not import_line.startswith("from ") and not import_line.startswith("import "): + if "." not in import_line: + return "import {0}".format(import_line) + parts = import_line.split(".") + end = parts.pop(-1) + return "from {0} import {1}".format(".".join(parts), end) + + return import_line + + def _skip_line(self, line): + skip_line = self._in_quote + if self.index == 1 and line.startswith("#"): + self._in_top_comment = True + return True + elif self._in_top_comment: + if not line.startswith("#") or line in self._section_comments: + self._in_top_comment = False + self._first_comment_index_end = self.index - 1 + + if '"' in line or "'" in line: + index = 0 + if self._first_comment_index_start == -1 and (line.startswith('"') or line.startswith("'")): + self._first_comment_index_start = self.index + while index < len(line): + if line[index] == "\\": + index += 1 + elif self._in_quote: + if line[index:index + len(self._in_quote)] == self._in_quote: + self._in_quote = False + if self._first_comment_index_end < self._first_comment_index_start: + self._first_comment_index_end = self.index + elif line[index] in ("'", '"'): + long_quote = line[index:index + 3] + if long_quote in ('"""', "'''"): + self._in_quote = long_quote + index += 2 + else: + self._in_quote = line[index] + elif line[index] == "#": + break + index += 1 + + return skip_line or self._in_quote or self._in_top_comment + + def _strip_syntax(self, import_string): + import_string = import_string.replace("_import", "[[i]]") + for remove_syntax in ['\\', '(', ')', ',']: + import_string = import_string.replace(remove_syntax, " ") + import_list = import_string.split() + for key in ('from', 'import'): + if key in import_list: + import_list.remove(key) + import_string = ' '.join(import_list) + import_string = import_string.replace("[[i]]", "_import") + return import_string.replace("{ ", "{|").replace(" }", "|}") + + def _parse(self): + """Parses a python file taking out and categorizing imports.""" + self._in_quote = False + self._in_top_comment = False + while not self._at_end(): + raw_line = line = self._get_line() + line = line.replace("from.import ", "from . import ") + line = line.replace("\t", " ").replace('import*', 'import *') + line = line.replace(" .import ", " . import ") + statement_index = self.index + skip_line = self._skip_line(line) + + if line in self._section_comments and not skip_line: + if self.import_index == -1: + self.import_index = self.index - 1 + continue + + if "isort:imports-" in line and line.startswith("#"): + section = line.split("isort:imports-")[-1].split()[0].upper() + self.place_imports[section] = [] + self.import_placements[line] = section + + if ";" in line: + for part in (part.strip() for part in line.split(";")): + if part and not part.startswith("from ") and not part.startswith("import "): + skip_line = True + + import_type = self._import_type(line) + if not import_type or skip_line: + self.out_lines.append(raw_line) + continue + + for line in (line.strip() for line in line.split(";")): + import_type = self._import_type(line) + if not import_type: + self.out_lines.append(line) + continue + + if self.import_index == -1: + self.import_index = self.index - 1 + nested_comments = {} + import_string, comments, new_comments = self._strip_comments(line) + stripped_line = [part for part in self._strip_syntax(import_string).strip().split(" ") if part] + if import_type == "from" and len(stripped_line) == 2 and stripped_line[1] != "*" and new_comments: + nested_comments[stripped_line[-1]] = comments[0] + + if "(" in line.split("#")[0] and not self._at_end(): + while not line.strip().endswith(")") and not self._at_end(): + line, comments, new_comments = self._strip_comments(self._get_line(), comments) + stripped_line = self._strip_syntax(line).strip() + if import_type == "from" and stripped_line and " " not in stripped_line and new_comments: + nested_comments[stripped_line] = comments[-1] + import_string += self.line_separator + line + else: + while line.strip().endswith("\\"): + line, comments, new_comments = self._strip_comments(self._get_line(), comments) + + # Still need to check for parentheses after an escaped line + if "(" in line.split("#")[0] and ")" not in line.split("#")[0] and not self._at_end(): + stripped_line = self._strip_syntax(line).strip() + if import_type == "from" and stripped_line and " " not in stripped_line and new_comments: + nested_comments[stripped_line] = comments[-1] + import_string += self.line_separator + line + + while not line.strip().endswith(")") and not self._at_end(): + line, comments, new_comments = self._strip_comments(self._get_line(), comments) + stripped_line = self._strip_syntax(line).strip() + if import_type == "from" and stripped_line and " " not in stripped_line and new_comments: + nested_comments[stripped_line] = comments[-1] + import_string += self.line_separator + line + + stripped_line = self._strip_syntax(line).strip() + if import_type == "from" and stripped_line and " " not in stripped_line and new_comments: + nested_comments[stripped_line] = comments[-1] + if import_string.strip().endswith(" import") or line.strip().startswith("import "): + import_string += self.line_separator + line + else: + import_string = import_string.rstrip().rstrip("\\") + " " + line.lstrip() + + if import_type == "from": + import_string = import_string.replace("import(", "import (") + parts = import_string.split(" import ") + from_import = parts[0].split(" ") + import_string = " import ".join([from_import[0] + " " + "".join(from_import[1:])] + parts[1:]) + + imports = [item.replace("{|", "{ ").replace("|}", " }") for item in + self._strip_syntax(import_string).split()] + if "as" in imports and (imports.index('as') + 1) < len(imports): + while "as" in imports: + index = imports.index('as') + if import_type == "from": + module = imports[0] + "." + imports[index - 1] + self.as_map[module] = imports[index + 1] + else: + module = imports[index - 1] + self.as_map[module] = imports[index + 1] + if not self.config['combine_as_imports']: + self.comments['straight'][module] = comments + comments = [] + del imports[index:index + 2] + if import_type == "from": + import_from = imports.pop(0) + placed_module = self.place_module(import_from) + if self.config['verbose']: + print("from-type place_module for %s returned %s" % (import_from, placed_module)) + if placed_module == '': + print( + "WARNING: could not place module {0} of line {1} --" + " Do you need to define a default section?".format(import_from, line) + ) + root = self.imports[placed_module][import_type] + for import_name in imports: + associated_comment = nested_comments.get(import_name) + if associated_comment: + self.comments['nested'].setdefault(import_from, {})[import_name] = associated_comment + comments.pop(comments.index(associated_comment)) + if comments: + self.comments['from'].setdefault(import_from, []).extend(comments) + + if len(self.out_lines) > max(self.import_index, self._first_comment_index_end + 1, 1) - 1: + last = self.out_lines and self.out_lines[-1].rstrip() or "" + while (last.startswith("#") and not last.endswith('"""') and not last.endswith("'''") and + 'isort:imports-' not in last): + self.comments['above']['from'].setdefault(import_from, []).insert(0, self.out_lines.pop(-1)) + if len(self.out_lines) > max(self.import_index - 1, self._first_comment_index_end + 1, 1) - 1: + last = self.out_lines[-1].rstrip() + else: + last = "" + if statement_index - 1 == self.import_index: + self.import_index -= len(self.comments['above']['from'].get(import_from, [])) + + if import_from not in root: + root[import_from] = OrderedDict() + root[import_from].update((module, None) for module in imports) + else: + for module in imports: + if comments: + self.comments['straight'][module] = comments + comments = None + + if len(self.out_lines) > max(self.import_index, self._first_comment_index_end + 1, 1) - 1: + + last = self.out_lines and self.out_lines[-1].rstrip() or "" + while (last.startswith("#") and not last.endswith('"""') and not last.endswith("'''") and + 'isort:imports-' not in last): + self.comments['above']['straight'].setdefault(module, []).insert(0, + self.out_lines.pop(-1)) + if len(self.out_lines) > 0 and len(self.out_lines) != self._first_comment_index_end: + last = self.out_lines[-1].rstrip() + else: + last = "" + if self.index - 1 == self.import_index: + self.import_index -= len(self.comments['above']['straight'].get(module, [])) + placed_module = self.place_module(module) + if self.config['verbose']: + print("else-type place_module for %s returned %s" % (module, placed_module)) + if placed_module == '': + print( + "WARNING: could not place module {0} of line {1} --" + " Do you need to define a default section?".format(import_from, line) + ) + self.imports[placed_module][import_type][module] = None + + +def coding_check(lines, default='utf-8'): + + # see https://www.python.org/dev/peps/pep-0263/ + pattern = re.compile(br'coding[:=]\s*([-\w.]+)') + + for line_number, line in enumerate(lines, 1): + groups = re.findall(pattern, line) + if groups: + return groups[0].decode('ascii') + if line_number > 2: + break + + return default diff --git a/Display/.venv/lib/python3.7/site-packages/isort/main.py b/Display/.venv/lib/python3.7/site-packages/isort/main.py new file mode 100644 index 0000000..fe36d11 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort/main.py @@ -0,0 +1,401 @@ +''' Tool for sorting imports alphabetically, and automatically separated into sections. + +Copyright (C) 2013 Timothy Edmund Crosley + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and +to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +''' +from __future__ import absolute_import, division, print_function, unicode_literals + +import argparse +import functools +import glob +import os +import re +import sys + +import setuptools + +from isort import SortImports, __version__ +from isort.settings import DEFAULT_SECTIONS, WrapModes, default, from_path, should_skip + +INTRO = r""" +/#######################################################################\ + + `sMMy` + .yyyy- ` + ##soos## ./o. + ` ``..-..` ``...`.`` ` ```` ``-ssso``` + .s:-y- .+osssssso/. ./ossss+:so+:` :+o-`/osso:+sssssssso/ + .s::y- osss+.``.`` -ssss+-.`-ossso` ssssso/::..::+ssss:::. + .s::y- /ssss+//:-.` `ssss+ `ssss+ sssso` :ssss` + .s::y- `-/+oossssso/ `ssss/ sssso ssss/ :ssss` + .y-/y- ````:ssss` ossso. :ssss: ssss/ :ssss. + `/so:` `-//::/osss+ `+ssss+-/ossso: /sso- `osssso/. + \/ `-/oooo++/- .:/++:/++/-` .. `://++/. + + + isort your Python imports for you so you don't have to + + VERSION {0} + +\########################################################################/ +""".format(__version__) + +shebang_re = re.compile(br'^#!.*\bpython[23w]?\b') + + +def is_python_file(path): + _root, ext = os.path.splitext(path) + if ext in ('.py', '.pyi'): + return True + if ext in ('.pex', ): + return False + + # Skip editor backup files. + if path.endswith('~'): + return False + + try: + with open(path, 'rb') as fp: + line = fp.readline(100) + except IOError: + return False + else: + return bool(shebang_re.match(line)) + + +class SortAttempt(object): + def __init__(self, incorrectly_sorted, skipped): + self.incorrectly_sorted = incorrectly_sorted + self.skipped = skipped + + +def sort_imports(file_name, **arguments): + try: + result = SortImports(file_name, **arguments) + return SortAttempt(result.incorrectly_sorted, result.skipped) + except IOError as e: + print("WARNING: Unable to parse file {0} due to {1}".format(file_name, e)) + return None + + +def iter_source_code(paths, config, skipped): + """Iterate over all Python source files defined in paths.""" + if 'not_skip' in config: + config['skip'] = list(set(config['skip']).difference(config['not_skip'])) + + for path in paths: + if os.path.isdir(path): + for dirpath, dirnames, filenames in os.walk(path, topdown=True, followlinks=True): + for dirname in list(dirnames): + if should_skip(dirname, config, dirpath): + skipped.append(dirname) + dirnames.remove(dirname) + for filename in filenames: + filepath = os.path.join(dirpath, filename) + if is_python_file(filepath): + relative_file = os.path.relpath(filepath, path) + if should_skip(relative_file, config, path): + skipped.append(filename) + else: + yield filepath + else: + yield path + + +class ISortCommand(setuptools.Command): + """The :class:`ISortCommand` class is used by setuptools to perform + imports checks on registered modules. + """ + + description = "Run isort on modules registered in setuptools" + user_options = [] + + def initialize_options(self): + default_settings = default.copy() + for key, value in default_settings.items(): + setattr(self, key, value) + + def finalize_options(self): + "Get options from config files." + self.arguments = {} + computed_settings = from_path(os.getcwd()) + for key, value in computed_settings.items(): + self.arguments[key] = value + + def distribution_files(self): + """Find distribution packages.""" + # This is verbatim from flake8 + if self.distribution.packages: + package_dirs = self.distribution.package_dir or {} + for package in self.distribution.packages: + pkg_dir = package + if package in package_dirs: + pkg_dir = package_dirs[package] + elif '' in package_dirs: + pkg_dir = package_dirs[''] + os.path.sep + pkg_dir + yield pkg_dir.replace('.', os.path.sep) + + if self.distribution.py_modules: + for filename in self.distribution.py_modules: + yield "%s.py" % filename + # Don't miss the setup.py file itself + yield "setup.py" + + def run(self): + arguments = self.arguments + wrong_sorted_files = False + arguments['check'] = True + for path in self.distribution_files(): + for python_file in glob.iglob(os.path.join(path, '*.py')): + try: + incorrectly_sorted = SortImports(python_file, **arguments).incorrectly_sorted + if incorrectly_sorted: + wrong_sorted_files = True + except IOError as e: + print("WARNING: Unable to parse file {0} due to {1}".format(python_file, e)) + if wrong_sorted_files: + sys.exit(1) + + +def parse_args(argv=None): + parser = argparse.ArgumentParser(description='Sort Python import definitions alphabetically ' + 'within logical sections. Run with no arguments to run ' + 'interactively. Run with `-` as the first argument to read from ' + 'stdin. Otherwise provide a list of files to sort.') + inline_args_group = parser.add_mutually_exclusive_group() + parser.add_argument('-a', '--add-import', dest='add_imports', action='append', + help='Adds the specified import line to all files, ' + 'automatically determining correct placement.') + parser.add_argument('-ac', '--atomic', dest='atomic', action='store_true', + help="Ensures the output doesn't save if the resulting file contains syntax errors.") + parser.add_argument('-af', '--force-adds', dest='force_adds', action='store_true', + help='Forces import adds even if the original file is empty.') + parser.add_argument('-b', '--builtin', dest='known_standard_library', action='append', + help='Force sortImports to recognize a module as part of the python standard library.') + parser.add_argument('-c', '--check-only', action='store_true', dest="check", + help='Checks the file for unsorted / unformatted imports and prints them to the ' + 'command line without modifying the file.') + parser.add_argument('-ca', '--combine-as', dest='combine_as_imports', action='store_true', + help="Combines as imports on the same line.") + parser.add_argument('-cs', '--combine-star', dest='combine_star', action='store_true', + help="Ensures that if a star import is present, nothing else is imported from that namespace.") + parser.add_argument('-d', '--stdout', help='Force resulting output to stdout, instead of in-place.', + dest='write_to_stdout', action='store_true') + parser.add_argument('-df', '--diff', dest='show_diff', action='store_true', + help="Prints a diff of all the changes isort would make to a file, instead of " + "changing it in place") + parser.add_argument('-ds', '--no-sections', help='Put all imports into the same section bucket', dest='no_sections', + action='store_true') + parser.add_argument('-dt', '--dont-order-by-type', dest='dont_order_by_type', + action='store_true', help='Only order imports alphabetically, do not attempt type ordering') + parser.add_argument('-e', '--balanced', dest='balanced_wrapping', action='store_true', + help='Balances wrapping to produce the most consistent line length possible') + parser.add_argument('-f', '--future', dest='known_future_library', action='append', + help='Force sortImports to recognize a module as part of the future compatibility libraries.') + parser.add_argument('-fas', '--force-alphabetical-sort', action='store_true', dest="force_alphabetical_sort", + help='Force all imports to be sorted as a single section') + parser.add_argument('-fass', '--force-alphabetical-sort-within-sections', action='store_true', + dest="force_alphabetical_sort", help='Force all imports to be sorted alphabetically within a ' + 'section') + parser.add_argument('-ff', '--from-first', dest='from_first', + help="Switches the typical ordering preference, showing from imports first then straight ones.") + parser.add_argument('-fgw', '--force-grid-wrap', nargs='?', const=2, type=int, dest="force_grid_wrap", + help='Force number of from imports (defaults to 2) to be grid wrapped regardless of line ' + 'length') + parser.add_argument('-fss', '--force-sort-within-sections', action='store_true', dest="force_sort_within_sections", + help='Force imports to be sorted by module, independent of import_type') + parser.add_argument('-i', '--indent', help='String to place for indents defaults to " " (4 spaces).', + dest='indent', type=str) + parser.add_argument('-j', '--jobs', help='Number of files to process in parallel.', + dest='jobs', type=int) + parser.add_argument('-k', '--keep-direct-and-as', dest='keep_direct_and_as_imports', action='store_true', + help="Turns off default behavior that removes direct imports when as imports exist.") + parser.add_argument('-l', '--lines', help='[Deprecated] The max length of an import line (used for wrapping ' + 'long imports).', + dest='line_length', type=int) + parser.add_argument('-lai', '--lines-after-imports', dest='lines_after_imports', type=int) + parser.add_argument('-lbt', '--lines-between-types', dest='lines_between_types', type=int) + parser.add_argument('-le', '--line-ending', dest='line_ending', + help="Forces line endings to the specified value. If not set, values will be guessed per-file.") + parser.add_argument('-ls', '--length-sort', help='Sort imports by their string length.', + dest='length_sort', action='store_true') + parser.add_argument('-m', '--multi-line', dest='multi_line_output', type=int, choices=range(len(WrapModes)), + help='Multi line output (0-grid, 1-vertical, 2-hanging, 3-vert-hanging, 4-vert-grid, ' + '5-vert-grid-grouped, 6-vert-grid-grouped-no-comma).') + inline_args_group.add_argument('-nis', '--no-inline-sort', dest='no_inline_sort', action='store_true', + help='Leaves `from` imports with multiple imports \'as-is\' (e.g. `from foo import a, c ,b`).') + parser.add_argument('-nlb', '--no-lines-before', help='Sections which should not be split with previous by empty lines', + dest='no_lines_before', action='append') + parser.add_argument('-ns', '--dont-skip', help='Files that sort imports should never skip over.', + dest='not_skip', action='append') + parser.add_argument('-o', '--thirdparty', dest='known_third_party', action='append', + help='Force sortImports to recognize a module as being part of a third party library.') + parser.add_argument('-ot', '--order-by-type', dest='order_by_type', + action='store_true', help='Order imports by type in addition to alphabetically') + parser.add_argument('-p', '--project', dest='known_first_party', action='append', + help='Force sortImports to recognize a module as being part of the current python project.') + parser.add_argument('-q', '--quiet', action='store_true', dest="quiet", + help='Shows extra quiet output, only errors are outputted.') + parser.add_argument('-r', dest='ambiguous_r_flag', action='store_true') + parser.add_argument('-rm', '--remove-import', dest='remove_imports', action='append', + help='Removes the specified import from all files.') + parser.add_argument('-rr', '--reverse-relative', dest='reverse_relative', action='store_true', + help='Reverse order of relative imports.') + parser.add_argument('-rc', '--recursive', dest='recursive', action='store_true', + help='Recursively look for Python files of which to sort imports') + parser.add_argument('-s', '--skip', help='Files that sort imports should skip over. If you want to skip multiple ' + 'files you should specify twice: --skip file1 --skip file2.', dest='skip', action='append') + parser.add_argument('-sd', '--section-default', dest='default_section', + help='Sets the default section for imports (by default FIRSTPARTY) options: ' + + str(DEFAULT_SECTIONS)) + parser.add_argument('-sg', '--skip-glob', help='Files that sort imports should skip over.', dest='skip_glob', + action='append') + inline_args_group.add_argument('-sl', '--force-single-line-imports', dest='force_single_line', action='store_true', + help='Forces all from imports to appear on their own line') + parser.add_argument('-sp', '--settings-path', dest="settings_path", + help='Explicitly set the settings path instead of auto determining based on file location.') + parser.add_argument('-t', '--top', help='Force specific imports to the top of their appropriate section.', + dest='force_to_top', action='append') + parser.add_argument('-tc', '--trailing-comma', dest='include_trailing_comma', action='store_true', + help='Includes a trailing comma on multi line imports that include parentheses.') + parser.add_argument('-up', '--use-parentheses', dest='use_parentheses', action='store_true', + help='Use parenthesis for line continuation on length limit instead of slashes.') + parser.add_argument('-v', '--version', action='store_true', dest='show_version') + parser.add_argument('-vb', '--verbose', action='store_true', dest="verbose", + help='Shows verbose output, such as when files are skipped or when a check is successful.') + parser.add_argument('--virtual-env', dest='virtual_env', + help='Virtual environment to use for determining whether a package is third-party') + parser.add_argument('--conda-env', dest='conda_env', + help='Conda environment to use for determining whether a package is third-party') + parser.add_argument('-vn', '--version-number', action='version', version=__version__, + help='Returns just the current version number without the logo') + parser.add_argument('-w', '--line-width', help='The max length of an import line (used for wrapping long imports).', + dest='line_length', type=int) + parser.add_argument('-wl', '--wrap-length', dest='wrap_length', + help="Specifies how long lines that are wrapped should be, if not set line_length is used.") + parser.add_argument('-ws', '--ignore-whitespace', action='store_true', dest="ignore_whitespace", + help='Tells isort to ignore whitespace differences when --check-only is being used.') + parser.add_argument('-y', '--apply', dest='apply', action='store_true', + help='Tells isort to apply changes recursively without asking') + parser.add_argument('--unsafe', dest='unsafe', action='store_true', + help='Tells isort to look for files in standard library directories, etc. ' + 'where it may not be safe to operate in') + parser.add_argument('--case-sensitive', dest='case_sensitive', action='store_true', + help='Tells isort to include casing when sorting module names') + parser.add_argument('--filter-files', dest='filter_files', action='store_true', + help='Tells isort to filter files even when they are explicitly passed in as part of the command') + parser.add_argument('files', nargs='*', help='One or more Python source files that need their imports sorted.') + + arguments = {key: value for key, value in vars(parser.parse_args(argv)).items() if value} + if 'dont_order_by_type' in arguments: + arguments['order_by_type'] = False + if arguments.pop('unsafe', False): + arguments['safety_excludes'] = False + return arguments + + +def main(argv=None): + arguments = parse_args(argv) + if arguments.get('show_version'): + print(INTRO) + return + + if arguments.get('ambiguous_r_flag'): + print('ERROR: Deprecated -r flag set. This flag has been replaced with -rm to remove ambiguity between it and ' + '-rc for recursive') + sys.exit(1) + + arguments['check_skip'] = False + if 'settings_path' in arguments: + sp = arguments['settings_path'] + arguments['settings_path'] = os.path.abspath(sp) if os.path.isdir(sp) else os.path.dirname(os.path.abspath(sp)) + if not os.path.isdir(arguments['settings_path']): + print("WARNING: settings_path dir does not exist: {0}".format(arguments['settings_path'])) + + if 'virtual_env' in arguments: + venv = arguments['virtual_env'] + arguments['virtual_env'] = os.path.abspath(venv) + if not os.path.isdir(arguments['virtual_env']): + print("WARNING: virtual_env dir does not exist: {0}".format(arguments['virtual_env'])) + + file_names = arguments.pop('files', []) + if file_names == ['-']: + try: + # python 3 + file_ = sys.stdin.buffer + except AttributeError: + # python 2 + file_ = sys.stdin + SortImports(file_=file_, write_to_stdout=True, **arguments) + else: + if not file_names: + file_names = ['.'] + arguments['recursive'] = True + if not arguments.get('apply', False): + arguments['ask_to_apply'] = True + + config = from_path(arguments.get('settings_path', '') or os.path.abspath(file_names[0]) or os.getcwd()).copy() + config.update(arguments) + wrong_sorted_files = False + skipped = [] + + if config.get('filter_files'): + filtered_files = [] + for file_name in file_names: + if should_skip(file_name, config): + skipped.append(file_name) + else: + filtered_files.append(file_name) + file_names = filtered_files + + if arguments.get('recursive', False): + file_names = iter_source_code(file_names, config, skipped) + num_skipped = 0 + if config['verbose'] or config.get('show_logo', False): + print(INTRO) + + jobs = arguments.get('jobs') + if jobs: + import multiprocessing + executor = multiprocessing.Pool(jobs) + attempt_iterator = executor.imap(functools.partial(sort_imports, **arguments), file_names) + else: + attempt_iterator = (sort_imports(file_name, **arguments) for file_name in file_names) + + for sort_attempt in attempt_iterator: + if not sort_attempt: + continue + incorrectly_sorted = sort_attempt.incorrectly_sorted + if arguments.get('check', False) and incorrectly_sorted: + wrong_sorted_files = True + if sort_attempt.skipped: + num_skipped += 1 + + if wrong_sorted_files: + sys.exit(1) + + num_skipped += len(skipped) + if num_skipped and not arguments.get('quiet', False): + if config['verbose']: + for was_skipped in skipped: + print("WARNING: {0} was skipped as it's listed in 'skip' setting" + " or matches a glob in 'skip_glob' setting".format(was_skipped)) + print("Skipped {0} files".format(num_skipped)) + + +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/isort/natural.py b/Display/.venv/lib/python3.7/site-packages/isort/natural.py new file mode 100644 index 0000000..c02b42c --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort/natural.py @@ -0,0 +1,47 @@ +"""isort/natural.py. + +Enables sorting strings that contain numbers naturally + +usage: + natural.nsorted(list) + +Copyright (C) 2013 Timothy Edmund Crosley + +Implementation originally from @HappyLeapSecond stack overflow user in response to: + https://stackoverflow.com/questions/5967500/how-to-correctly-sort-a-string-with-a-number-inside + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and +to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +""" +import re + + +def _atoi(text): + return int(text) if text.isdigit() else text + + +def _natural_keys(text): + return [_atoi(c) for c in re.split(r'(\d+)', text)] + + +def nsorted(to_sort, key=None): + """Returns a naturally sorted list""" + if key is None: + key_callback = _natural_keys + else: + def key_callback(item): + return _natural_keys(key(item)) + + return sorted(to_sort, key=key_callback) diff --git a/Display/.venv/lib/python3.7/site-packages/isort/pie_slice.py b/Display/.venv/lib/python3.7/site-packages/isort/pie_slice.py new file mode 100644 index 0000000..569ea76 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort/pie_slice.py @@ -0,0 +1,154 @@ +"""pie_slice/overrides.py. + +Overrides Python syntax to conform to the Python3 version as much as possible using a '*' import + +Copyright (C) 2013 Timothy Edmund Crosley + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and +to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +""" +from __future__ import absolute_import + +import collections +import sys + +__version__ = "1.1.0" + +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +VERSION = sys.version_info + +__all__ = ['PY2', 'PY3', 'lru_cache', 'apply_changes_to_python_environment'] + + +if PY3: + input = input + + def apply_changes_to_python_environment(): + pass +else: + input = raw_input # noqa: F821 + + python_environment_changes_applied = False + + import sys + stdout = sys.stdout + stderr = sys.stderr + + def apply_changes_to_python_environment(): + global python_environment_changes_applied + if python_environment_changes_applied or sys.getdefaultencoding() == 'utf-8': + python_environment_changes_applied = True + return + + try: + reload(sys) + sys.stdout = stdout + sys.stderr = stderr + sys.setdefaultencoding('utf-8') + except NameError: # Python 3 + sys.exit('This should not happen!') + + python_environment_changes_applied = True + + +if sys.version_info < (3, 2): + try: + from threading import Lock + except ImportError: + from dummy_threading import Lock + + from functools import wraps + + _CacheInfo = collections.namedtuple("CacheInfo", "hits misses maxsize currsize") + + def lru_cache(maxsize=100): + """Least-recently-used cache decorator. + Taking from: https://github.com/MiCHiLU/python-functools32/blob/master/functools32/functools32.py + with slight modifications. + If *maxsize* is set to None, the LRU features are disabled and the cache + can grow without bound. + Arguments to the cached function must be hashable. + View the cache statistics named tuple (hits, misses, maxsize, currsize) with + f.cache_info(). Clear the cache and statistics with f.cache_clear(). + Access the underlying function with f.__wrapped__. + See: https://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used + + """ + def decorating_function(user_function, tuple=tuple, sorted=sorted, len=len, KeyError=KeyError): + hits, misses = [0], [0] + kwd_mark = (object(),) # separates positional and keyword args + lock = Lock() + + if maxsize is None: + CACHE = {} + + @wraps(user_function) + def wrapper(*args, **kwds): + key = args + if kwds: + key += kwd_mark + tuple(sorted(kwds.items())) + try: + result = CACHE[key] + hits[0] += 1 + return result + except KeyError: + pass + result = user_function(*args, **kwds) + CACHE[key] = result + misses[0] += 1 + return result + else: + CACHE = collections.OrderedDict() + + @wraps(user_function) + def wrapper(*args, **kwds): + key = args + if kwds: + key += kwd_mark + tuple(sorted(kwds.items())) + with lock: + cached = CACHE.get(key, None) + if cached: + del CACHE[key] + CACHE[key] = cached + hits[0] += 1 + return cached + result = user_function(*args, **kwds) + with lock: + CACHE[key] = result # record recent use of this key + misses[0] += 1 + while len(CACHE) > maxsize: + CACHE.popitem(last=False) + return result + + def cache_info(): + """Report CACHE statistics.""" + with lock: + return _CacheInfo(hits[0], misses[0], maxsize, len(CACHE)) + + def cache_clear(): + """Clear the CACHE and CACHE statistics.""" + with lock: + CACHE.clear() + hits[0] = misses[0] = 0 + + wrapper.cache_info = cache_info + wrapper.cache_clear = cache_clear + return wrapper + + return decorating_function + +else: + from functools import lru_cache diff --git a/Display/.venv/lib/python3.7/site-packages/isort/pylama_isort.py b/Display/.venv/lib/python3.7/site-packages/isort/pylama_isort.py new file mode 100644 index 0000000..6fa235f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort/pylama_isort.py @@ -0,0 +1,29 @@ +import os +import sys + +from pylama.lint import Linter as BaseLinter + +from .isort import SortImports + + +class Linter(BaseLinter): + + def allow(self, path): + """Determine if this path should be linted.""" + return path.endswith('.py') + + def run(self, path, **meta): + """Lint the file. Return an array of error dicts if appropriate.""" + with open(os.devnull, 'w') as devnull: + # Suppress isort messages + sys.stdout = devnull + + if SortImports(path, check=True).incorrectly_sorted: + return [{ + 'lnum': 0, + 'col': 0, + 'text': 'Incorrectly sorted imports.', + 'type': 'ISORT' + }] + else: + return [] diff --git a/Display/.venv/lib/python3.7/site-packages/isort/settings.py b/Display/.venv/lib/python3.7/site-packages/isort/settings.py new file mode 100644 index 0000000..a69471e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort/settings.py @@ -0,0 +1,356 @@ +"""isort/settings.py. + +Defines how the default settings for isort should be loaded + +(First from the default setting dictionary at the top of the file, then overridden by any settings + in ~/.isort.cfg or $XDG_CONFIG_HOME/isort.cfg if there are any) + +Copyright (C) 2013 Timothy Edmund Crosley + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and +to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +""" +from __future__ import absolute_import, division, print_function, unicode_literals + +import fnmatch +import io +import os +import posixpath +import re +import sys +import warnings +from collections import namedtuple +from distutils.util import strtobool + +from .pie_slice import lru_cache +from .utils import difference, union + +try: + import configparser +except ImportError: + import ConfigParser as configparser + +try: + import toml +except ImportError: + toml = False + +try: + import appdirs + if appdirs.system == 'darwin': + appdirs.system = 'linux2' +except ImportError: + appdirs = None + +MAX_CONFIG_SEARCH_DEPTH = 25 # The number of parent directories isort will look for a config file within +DEFAULT_SECTIONS = ('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER') + +safety_exclude_re = re.compile( + r"/(\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|_build|buck-out|build|dist|\.pants\.d" + r"|lib/python[0-9].[0-9]+)/" +) + +WrapModes = ('GRID', 'VERTICAL', 'HANGING_INDENT', 'VERTICAL_HANGING_INDENT', 'VERTICAL_GRID', 'VERTICAL_GRID_GROUPED', + 'VERTICAL_GRID_GROUPED_NO_COMMA', 'NOQA') +WrapModes = namedtuple('WrapModes', WrapModes)(*range(len(WrapModes))) + +# Note that none of these lists must be complete as they are simply fallbacks for when included auto-detection fails. +default = {'force_to_top': [], + 'skip': [], + 'skip_glob': [], + 'line_length': 79, + 'wrap_length': 0, + 'line_ending': None, + 'sections': DEFAULT_SECTIONS, + 'no_sections': False, + 'known_future_library': ['__future__'], + 'known_standard_library': ['AL', 'BaseHTTPServer', 'Bastion', 'CGIHTTPServer', 'Carbon', 'ColorPicker', + 'ConfigParser', 'Cookie', 'DEVICE', 'DocXMLRPCServer', 'EasyDialogs', 'FL', + 'FrameWork', 'GL', 'HTMLParser', 'MacOS', 'MimeWriter', 'MiniAEFrame', 'Nav', + 'PixMapWrapper', 'Queue', 'SUNAUDIODEV', 'ScrolledText', 'SimpleHTTPServer', + 'SimpleXMLRPCServer', 'SocketServer', 'StringIO', 'Tix', 'Tkinter', 'UserDict', + 'UserList', 'UserString', 'W', '__builtin__', 'abc', 'aepack', 'aetools', + 'aetypes', 'aifc', 'al', 'anydbm', 'applesingle', 'argparse', 'array', 'ast', + 'asynchat', 'asyncio', 'asyncore', 'atexit', 'audioop', 'autoGIL', 'base64', + 'bdb', 'binascii', 'binhex', 'bisect', 'bsddb', 'buildtools', 'builtins', + 'bz2', 'cPickle', 'cProfile', 'cStringIO', 'calendar', 'cd', 'cfmfile', 'cgi', + 'cgitb', 'chunk', 'cmath', 'cmd', 'code', 'codecs', 'codeop', 'collections', + 'colorsys', 'commands', 'compileall', 'compiler', 'concurrent', 'configparser', + 'contextlib', 'contextvars', 'cookielib', 'copy', 'copy_reg', 'copyreg', 'crypt', 'csv', + 'ctypes', 'curses', 'dataclasses', 'datetime', 'dbhash', 'dbm', 'decimal', 'difflib', + 'dircache', 'dis', 'distutils', 'dl', 'doctest', 'dumbdbm', 'dummy_thread', + 'dummy_threading', 'email', 'encodings', 'ensurepip', 'enum', 'errno', + 'exceptions', 'faulthandler', 'fcntl', 'filecmp', 'fileinput', 'findertools', + 'fl', 'flp', 'fm', 'fnmatch', 'formatter', 'fpectl', 'fpformat', 'fractions', + 'ftplib', 'functools', 'future_builtins', 'gc', 'gdbm', 'gensuitemodule', + 'getopt', 'getpass', 'gettext', 'gl', 'glob', 'grp', 'gzip', 'hashlib', + 'heapq', 'hmac', 'hotshot', 'html', 'htmlentitydefs', 'htmllib', 'http', + 'httplib', 'ic', 'icopen', 'imageop', 'imaplib', 'imgfile', 'imghdr', 'imp', + 'importlib', 'imputil', 'inspect', 'io', 'ipaddress', 'itertools', 'jpeg', + 'json', 'keyword', 'lib2to3', 'linecache', 'locale', 'logging', 'lzma', + 'macerrors', 'macostools', 'macpath', 'macresource', 'mailbox', 'mailcap', + 'marshal', 'math', 'md5', 'mhlib', 'mimetools', 'mimetypes', 'mimify', 'mmap', + 'modulefinder', 'msilib', 'msvcrt', 'multifile', 'multiprocessing', 'mutex', + 'netrc', 'new', 'nis', 'nntplib', 'numbers', 'operator', 'optparse', 'os', + 'ossaudiodev', 'parser', 'pathlib', 'pdb', 'pickle', 'pickletools', 'pipes', + 'pkgutil', 'platform', 'plistlib', 'popen2', 'poplib', 'posix', 'posixfile', + 'pprint', 'profile', 'pstats', 'pty', 'pwd', 'py_compile', 'pyclbr', 'pydoc', + 'queue', 'quopri', 'random', 're', 'readline', 'reprlib', 'resource', 'rexec', + 'rfc822', 'rlcompleter', 'robotparser', 'runpy', 'sched', 'secrets', 'select', + 'selectors', 'sets', 'sgmllib', 'sha', 'shelve', 'shlex', 'shutil', 'signal', + 'site', 'sitecustomize', 'smtpd', 'smtplib', 'sndhdr', 'socket', 'socketserver', + 'spwd', 'sqlite3', 'ssl', 'stat', 'statistics', 'statvfs', 'string', 'stringprep', + 'struct', 'subprocess', 'sunau', 'sunaudiodev', 'symbol', 'symtable', 'sys', + 'sysconfig', 'syslog', 'tabnanny', 'tarfile', 'telnetlib', 'tempfile', 'termios', + 'test', 'textwrap', 'this', 'thread', 'threading', 'time', 'timeit', 'tkinter', + 'token', 'tokenize', 'trace', 'traceback', 'tracemalloc', 'ttk', 'tty', 'turtle', + 'turtledemo', 'types', 'typing', 'unicodedata', 'unittest', 'urllib', 'urllib2', + 'urlparse', 'usercustomize', 'uu', 'uuid', 'venv', 'videoreader', + 'warnings', 'wave', 'weakref', 'webbrowser', 'whichdb', 'winreg', 'winsound', + 'wsgiref', 'xdrlib', 'xml', 'xmlrpc', 'xmlrpclib', 'zipapp', 'zipfile', + 'zipimport', 'zlib'], + 'known_third_party': ['google.appengine.api'], + 'known_first_party': [], + 'multi_line_output': WrapModes.GRID, + 'forced_separate': [], + 'indent': ' ' * 4, + 'comment_prefix': ' #', + 'length_sort': False, + 'add_imports': [], + 'remove_imports': [], + 'reverse_relative': False, + 'force_single_line': False, + 'default_section': 'FIRSTPARTY', + 'import_heading_future': '', + 'import_heading_stdlib': '', + 'import_heading_thirdparty': '', + 'import_heading_firstparty': '', + 'import_heading_localfolder': '', + 'balanced_wrapping': False, + 'use_parentheses': False, + 'order_by_type': True, + 'atomic': False, + 'lines_after_imports': -1, + 'lines_between_sections': 1, + 'lines_between_types': 0, + 'combine_as_imports': False, + 'combine_star': False, + 'keep_direct_and_as_imports': False, + 'include_trailing_comma': False, + 'from_first': False, + 'verbose': False, + 'quiet': False, + 'force_adds': False, + 'force_alphabetical_sort_within_sections': False, + 'force_alphabetical_sort': False, + 'force_grid_wrap': 0, + 'force_sort_within_sections': False, + 'show_diff': False, + 'ignore_whitespace': False, + 'no_lines_before': [], + 'no_inline_sort': False, + 'ignore_comments': False, + 'safety_excludes': True, + 'case_sensitive': False} + + +@lru_cache() +def from_path(path): + computed_settings = default.copy() + isort_defaults = ['~/.isort.cfg'] + if appdirs: + isort_defaults = [appdirs.user_config_dir('isort.cfg')] + isort_defaults + + _update_settings_with_config(path, '.editorconfig', ['~/.editorconfig'], ('*', '*.py', '**.py'), computed_settings) + _update_settings_with_config(path, 'pyproject.toml', [], ('tool.isort', ), computed_settings) + _update_settings_with_config(path, '.isort.cfg', isort_defaults, ('settings', 'isort'), computed_settings) + _update_settings_with_config(path, 'setup.cfg', [], ('isort', 'tool:isort'), computed_settings) + _update_settings_with_config(path, 'tox.ini', [], ('isort', 'tool:isort'), computed_settings) + return computed_settings + + +def _update_settings_with_config(path, name, default, sections, computed_settings): + editor_config_file = None + for potential_settings_path in default: + expanded = os.path.expanduser(potential_settings_path) + if os.path.exists(expanded): + editor_config_file = expanded + break + + tries = 0 + current_directory = path + while current_directory and tries < MAX_CONFIG_SEARCH_DEPTH: + potential_path = os.path.join(current_directory, str(name)) + if os.path.exists(potential_path): + editor_config_file = potential_path + break + + new_directory = os.path.split(current_directory)[0] + if current_directory == new_directory: + break + current_directory = new_directory + tries += 1 + + if editor_config_file and os.path.exists(editor_config_file): + _update_with_config_file(editor_config_file, sections, computed_settings) + + +def _update_with_config_file(file_path, sections, computed_settings): + cwd = os.path.dirname(file_path) + settings = _get_config_data(file_path, sections).copy() + if not settings: + return + + if file_path.endswith('.editorconfig'): + indent_style = settings.pop('indent_style', '').strip() + indent_size = settings.pop('indent_size', '').strip() + if indent_size == "tab": + indent_size = settings.pop('tab_width', '').strip() + + if indent_style == 'space': + computed_settings['indent'] = ' ' * (indent_size and int(indent_size) or 4) + elif indent_style == 'tab': + computed_settings['indent'] = '\t' * (indent_size and int(indent_size) or 1) + + max_line_length = settings.pop('max_line_length', '').strip() + if max_line_length: + computed_settings['line_length'] = float('inf') if max_line_length == 'off' else int(max_line_length) + + for key, value in settings.items(): + access_key = key.replace('not_', '').lower() + existing_value_type = type(default.get(access_key, '')) + if existing_value_type in (list, tuple): + # sections has fixed order values; no adding or substraction from any set + if access_key == 'sections': + computed_settings[access_key] = tuple(_as_list(value)) + else: + existing_data = set(computed_settings.get(access_key, default.get(access_key))) + if key.startswith('not_'): + computed_settings[access_key] = difference(existing_data, _as_list(value)) + elif key.startswith('known_'): + computed_settings[access_key] = union(existing_data, _abspaths(cwd, _as_list(value))) + else: + computed_settings[access_key] = union(existing_data, _as_list(value)) + elif existing_value_type == bool: + # Only some configuration formats support native boolean values. + if not isinstance(value, bool): + value = bool(strtobool(value)) + computed_settings[access_key] = value + elif key.startswith('known_'): + computed_settings[access_key] = list(_abspaths(cwd, _as_list(value))) + elif key == 'force_grid_wrap': + try: + result = existing_value_type(value) + except ValueError: + # backwards compat + result = default.get(access_key) if value.lower().strip() == 'false' else 2 + computed_settings[access_key] = result + else: + computed_settings[access_key] = existing_value_type(value) + + +def _as_list(value): + if not isinstance(value, list): + value = value.replace('\n', ',').split(',') + + return filter(bool, [item.strip() for item in value]) + + +def _abspaths(cwd, values): + paths = [ + os.path.join(cwd, value) + if not value.startswith(os.path.sep) and value.endswith(os.path.sep) + else value + for value in values + ] + return paths + + +@lru_cache() +def _get_config_data(file_path, sections): + settings = {} + + with io.open(file_path) as config_file: + if file_path.endswith('.toml'): + if toml: + config = toml.load(config_file) + for section in sections: + config_section = config + for key in section.split('.'): + config_section = config_section.get(key, {}) + settings.update(config_section) + else: + if '[tool.isort]' in config_file.read(): + warnings.warn("Found {} with [tool.isort] section, but toml package is not installed. " + "To configure isort with {}, install with 'isort[pyproject]'.".format(file_path, + file_path)) + else: + if file_path.endswith('.editorconfig'): + line = '\n' + last_position = config_file.tell() + while line: + line = config_file.readline() + if '[' in line: + config_file.seek(last_position) + break + last_position = config_file.tell() + + if sys.version_info >= (3, 2): + config = configparser.ConfigParser(strict=False) + config.read_file(config_file) + else: + config = configparser.SafeConfigParser() + config.readfp(config_file) + + for section in sections: + if config.has_section(section): + settings.update(config.items(section)) + + return settings + + +def should_skip(filename, config, path=''): + """Returns True if the file and/or folder should be skipped based on the passed in settings.""" + os_path = os.path.join(path, filename) + + normalized_path = os_path.replace('\\', '/') + if normalized_path[1:2] == ':': + normalized_path = normalized_path[2:] + + if path and config['safety_excludes']: + check_exclude = '/' + filename.replace('\\', '/') + '/' + if path and os.path.basename(path) in ('lib', ): + check_exclude = '/' + os.path.basename(path) + check_exclude + if safety_exclude_re.search(check_exclude): + return True + + for skip_path in config['skip']: + if posixpath.abspath(normalized_path) == posixpath.abspath(skip_path.replace('\\', '/')): + return True + + position = os.path.split(filename) + while position[1]: + if position[1] in config['skip']: + return True + position = os.path.split(position[0]) + + for glob in config['skip_glob']: + if fnmatch.fnmatch(filename, glob) or fnmatch.fnmatch('/' + filename, glob): + return True + + if not (os.path.isfile(os_path) or os.path.isdir(os_path) or os.path.islink(os_path)): + return True + + return False diff --git a/Display/.venv/lib/python3.7/site-packages/isort/utils.py b/Display/.venv/lib/python3.7/site-packages/isort/utils.py new file mode 100644 index 0000000..ce4e588 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/isort/utils.py @@ -0,0 +1,53 @@ +import os +import sys +from contextlib import contextmanager + + +def exists_case_sensitive(path): + """ + Returns if the given path exists and also matches the case on Windows. + + When finding files that can be imported, it is important for the cases to match because while + file os.path.exists("module.py") and os.path.exists("MODULE.py") both return True on Windows, Python + can only import using the case of the real file. + """ + result = os.path.exists(path) + if (sys.platform.startswith('win') or sys.platform == 'darwin') and result: + directory, basename = os.path.split(path) + result = basename in os.listdir(directory) + return result + + +@contextmanager +def chdir(path): + """Context manager for changing dir and restoring previous workdir after exit. + """ + curdir = os.getcwd() + os.chdir(path) + try: + yield + finally: + os.chdir(curdir) + + +def union(a, b): + """ Return a list of items that are in `a` or `b` + """ + u = [] + for item in a: + if item not in u: + u.append(item) + for item in b: + if item not in u: + u.append(item) + return u + + +def difference(a, b): + """ Return a list of items from `a` that are not in `b`. + """ + d = [] + for item in a: + if item not in b: + d.append(item) + return d diff --git a/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy-1.4.3.dist-info/INSTALLER b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy-1.4.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy-1.4.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy-1.4.3.dist-info/METADATA b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy-1.4.3.dist-info/METADATA new file mode 100644 index 0000000..6b4b830 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy-1.4.3.dist-info/METADATA @@ -0,0 +1,166 @@ +Metadata-Version: 2.1 +Name: lazy-object-proxy +Version: 1.4.3 +Summary: A fast and thorough lazy object proxy. +Home-page: https://github.com/ionelmc/python-lazy-object-proxy +Author: Ionel Cristian Mărieș +Author-email: contact@ionelmc.ro +License: BSD-2-Clause +Project-URL: Documentation, https://python-lazy-object-proxy.readthedocs.io/ +Project-URL: Changelog, https://python-lazy-object-proxy.readthedocs.io/en/latest/changelog.html +Project-URL: Issue Tracker, https://github.com/ionelmc/python-lazy-object-proxy/issues +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: Unix +Classifier: Operating System :: POSIX +Classifier: Operating System :: Microsoft :: Windows +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Utilities +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* + +======== +Overview +======== + + + +A fast and thorough lazy object proxy. + +* Free software: BSD 2-Clause License + +Note that this is based on `wrapt`_'s ObjectProxy with one big change: it calls a function the first time the proxy object is +used, while `wrapt.ObjectProxy` just forwards the method calls to the target object. + +In other words, you use `lazy-object-proxy` when you only have the object way later and you use `wrapt.ObjectProxy` when you +want to override few methods (by subclassing) and forward everything else to the target object. + +Example:: + + import lazy_object_proxy + + def expensive_func(): + from time import sleep + print('starting calculation') + # just as example for a very slow computation + sleep(2) + print('finished calculation') + # return the result of the calculation + return 10 + + obj = lazy_object_proxy.Proxy(expensive_func) + # function is called only when object is actually used + print(obj) # now expensive_func is called + + print(obj) # the result without calling the expensive_func + +Installation +============ + +:: + + pip install lazy-object-proxy + +Documentation +============= + +https://python-lazy-object-proxy.readthedocs.io/ + +Development +=========== + +To run the all tests run:: + + tox + +Acknowledgements +================ + +This project is based on some code from `wrapt`_ as you can see in the git history. + +.. _wrapt: https://github.com/GrahamDumpleton/wrapt + + +Changelog +========= + +1.4.3 (2019-10-26) +------------------ + +* Added binary wheels for Python 3.8. +* Fixed license metadata. + +1.4.2 (2019-08-22) +------------------ + +* Included a ``pyproject.toml`` to allow users install the sdist with old python/setuptools, as the + setuptools-scm dep will be fetched by pip instead of setuptools. + Fixes `#30 `_. + +1.4.1 (2019-05-10) +------------------ + +* Fixed wheels being built with ``-coverage`` cflags. No more issues about bogus ``cext.gcda`` files. +* Removed useless C file from wheels. +* Changed ``setup.py`` to use setuptools-scm. + +1.4.0 (2019-05-05) +------------------ + +* Fixed ``__mod__`` for the slots backend. Contributed by Ran Benita in + `#28 `_. +* Dropped support for Python 2.6 and 3.3. Contributed by "hugovk" in + `#24 `_. + +1.3.1 (2017-05-05) +------------------ + +* Fix broken release (``sdist`` had a broken ``MANIFEST.in``). + +1.3.0 (2017-05-02) +------------------ + +* Speed up arithmetic operations involving ``cext.Proxy`` subclasses. + +1.2.2 (2016-04-14) +------------------ + +* Added `manylinux `_ wheels. +* Minor cleanup in readme. + +1.2.1 (2015-08-18) +------------------ + +* Fix a memory leak (the wrapped object would get bogus references). Contributed by Astrum Kuo in + `#10 `_. + +1.2.0 (2015-07-06) +------------------ + +* Don't instantiate the object when __repr__ is called. This aids with debugging (allows one to see exactly in + what state the proxy is). + +1.1.0 (2015-07-05) +------------------ + +* Added support for pickling. The pickled value is going to be the wrapped object *without* any Proxy container. +* Fixed a memory management issue in the C extension (reference cycles weren't garbage collected due to improper + handling in the C extension). Contributed by Alvin Chow in + `#8 `_. + +1.0.2 (2015-04-11) +----------------------------------------- + +* First release on PyPI. + + diff --git a/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy-1.4.3.dist-info/RECORD b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy-1.4.3.dist-info/RECORD new file mode 100644 index 0000000..6997fa6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy-1.4.3.dist-info/RECORD @@ -0,0 +1,18 @@ +lazy_object_proxy-1.4.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +lazy_object_proxy-1.4.3.dist-info/METADATA,sha256=Y2X63wcFbQT4yI3zKpRFwfpA_TCpB6U79MPmRGCMJT0,5088 +lazy_object_proxy-1.4.3.dist-info/RECORD,, +lazy_object_proxy-1.4.3.dist-info/WHEEL,sha256=AhV6RMqZ2IDfreRJKo44QWYxYeP-0Jr0bezzBLQ1eog,109 +lazy_object_proxy-1.4.3.dist-info/top_level.txt,sha256=UNH-FQB-j_8bYqPz3gD90kHvaC42TQqY0thHSnbaa0k,18 +lazy_object_proxy/__init__.py,sha256=pMqxzToF24DuzOltm-Q8nZ3jNDXOaSQcJmiNArdUrlU,410 +lazy_object_proxy/__pycache__/__init__.cpython-37.pyc,, +lazy_object_proxy/__pycache__/_version.cpython-37.pyc,, +lazy_object_proxy/__pycache__/compat.cpython-37.pyc,, +lazy_object_proxy/__pycache__/simple.cpython-37.pyc,, +lazy_object_proxy/__pycache__/slots.cpython-37.pyc,, +lazy_object_proxy/__pycache__/utils.cpython-37.pyc,, +lazy_object_proxy/_version.py,sha256=ALahZZsa-Z4ZoXe2iDIs4ajidCOW695W5leOWijWYEc,116 +lazy_object_proxy/cext.cpython-37m-x86_64-linux-gnu.so,sha256=nw8QWBFVIuGAzbmYxnimtrp7_QwgGtOsjlUcLk4_8w0,162304 +lazy_object_proxy/compat.py,sha256=DY3HbKwbrbeKY6tkXRNRLJ1go6HJb8HUwWqyw3T5g_g,196 +lazy_object_proxy/simple.py,sha256=guacy8_QbJeBs7vXpPPVoDVkDNXOZ86xyS1dtAeKvOs,8216 +lazy_object_proxy/slots.py,sha256=9DilWUINScpZN26NwmRtscTtaqaEwtCfIoriLq5Nz24,11359 +lazy_object_proxy/utils.py,sha256=x4XTrtlp_mDTWO_EOq_ILIOv2Qol8RLMnRm5M8l3OfU,291 diff --git a/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy-1.4.3.dist-info/WHEEL b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy-1.4.3.dist-info/WHEEL new file mode 100644 index 0000000..697e432 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy-1.4.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.31.1) +Root-Is-Purelib: false +Tag: cp37-cp37m-manylinux1_x86_64 + diff --git a/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy-1.4.3.dist-info/top_level.txt b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy-1.4.3.dist-info/top_level.txt new file mode 100644 index 0000000..bdf032e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy-1.4.3.dist-info/top_level.txt @@ -0,0 +1 @@ +lazy_object_proxy diff --git a/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/__init__.py b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/__init__.py new file mode 100644 index 0000000..e9a9a76 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/__init__.py @@ -0,0 +1,23 @@ +try: + import copy_reg as copyreg +except ImportError: + import copyreg + +from .utils import identity + +copyreg.constructor(identity) + +try: + from .cext import Proxy + from .cext import identity +except ImportError: + from .slots import Proxy +else: + copyreg.constructor(identity) + +try: + from ._version import version as __version__ +except ImportError: + __version__ = '1.4.3' + +__all__ = "Proxy", diff --git a/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/_version.py b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/_version.py new file mode 100644 index 0000000..3136771 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/_version.py @@ -0,0 +1,4 @@ +# coding: utf-8 +# file generated by setuptools_scm +# don't change, don't track in version control +version = '1.4.3' diff --git a/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/cext.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/cext.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..11d0106 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/cext.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/compat.py b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/compat.py new file mode 100644 index 0000000..dc6edfa --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/compat.py @@ -0,0 +1,9 @@ +import sys + +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + return meta("NewBase", bases, {}) diff --git a/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/simple.py b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/simple.py new file mode 100644 index 0000000..24b1339 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/simple.py @@ -0,0 +1,246 @@ +import operator + +from .compat import PY2 +from .compat import PY3 +from .compat import with_metaclass +from .utils import cached_property +from .utils import identity + + +def make_proxy_method(code): + def proxy_wrapper(self, *args): + return code(self.__wrapped__, *args) + + return proxy_wrapper + + +class _ProxyMethods(object): + # We use properties to override the values of __module__ and + # __doc__. If we add these in ObjectProxy, the derived class + # __dict__ will still be setup to have string variants of these + # attributes and the rules of descriptors means that they appear to + # take precedence over the properties in the base class. To avoid + # that, we copy the properties into the derived class type itself + # via a meta class. In that way the properties will always take + # precedence. + + @property + def __module__(self): + return self.__wrapped__.__module__ + + @__module__.setter + def __module__(self, value): + self.__wrapped__.__module__ = value + + @property + def __doc__(self): + return self.__wrapped__.__doc__ + + @__doc__.setter + def __doc__(self, value): + self.__wrapped__.__doc__ = value + + # Need to also propagate the special __weakref__ attribute for case + # where decorating classes which will define this. If do not define + # it and use a function like inspect.getmembers() on a decorator + # class it will fail. This can't be in the derived classes. + + @property + def __weakref__(self): + return self.__wrapped__.__weakref__ + + +class _ProxyMetaType(type): + def __new__(cls, name, bases, dictionary): + # Copy our special properties into the class so that they + # always take precedence over attributes of the same name added + # during construction of a derived class. This is to save + # duplicating the implementation for them in all derived classes. + + dictionary.update(vars(_ProxyMethods)) + dictionary.pop('__dict__') + + return type.__new__(cls, name, bases, dictionary) + + +class Proxy(with_metaclass(_ProxyMetaType)): + __factory__ = None + + def __init__(self, factory): + self.__dict__['__factory__'] = factory + + @cached_property + def __wrapped__(self): + self = self.__dict__ + if '__factory__' in self: + factory = self['__factory__'] + return factory() + else: + raise ValueError("Proxy hasn't been initiated: __factory__ is missing.") + + __name__ = property(make_proxy_method(operator.attrgetter('__name__'))) + __class__ = property(make_proxy_method(operator.attrgetter('__class__'))) + __annotations__ = property(make_proxy_method(operator.attrgetter('__anotations__'))) + __dir__ = make_proxy_method(dir) + __str__ = make_proxy_method(str) + + if PY3: + __bytes__ = make_proxy_method(bytes) + + def __repr__(self, __getattr__=object.__getattribute__): + if '__wrapped__' in self.__dict__: + return '<{} at 0x{:x} wrapping {!r} at 0x{:x} with factory {!r}>'.format( + type(self).__name__, id(self), + self.__wrapped__, id(self.__wrapped__), + self.__factory__ + ) + else: + return '<{} at 0x{:x} with factory {!r}>'.format( + type(self).__name__, id(self), + self.__factory__ + ) + + __reversed__ = make_proxy_method(reversed) + + if PY3: + __round__ = make_proxy_method(round) + + __lt__ = make_proxy_method(operator.lt) + __le__ = make_proxy_method(operator.le) + __eq__ = make_proxy_method(operator.eq) + __ne__ = make_proxy_method(operator.ne) + __gt__ = make_proxy_method(operator.gt) + __ge__ = make_proxy_method(operator.ge) + __hash__ = make_proxy_method(hash) + __nonzero__ = make_proxy_method(bool) + __bool__ = make_proxy_method(bool) + + def __setattr__(self, name, value): + if hasattr(type(self), name): + self.__dict__[name] = value + else: + setattr(self.__wrapped__, name, value) + + def __getattr__(self, name): + if name in ('__wrapped__', '__factory__'): + raise AttributeError(name) + else: + return getattr(self.__wrapped__, name) + + def __delattr__(self, name): + if hasattr(type(self), name): + del self.__dict__[name] + else: + delattr(self.__wrapped__, name) + + __add__ = make_proxy_method(operator.add) + __sub__ = make_proxy_method(operator.sub) + __mul__ = make_proxy_method(operator.mul) + __div__ = make_proxy_method(operator.div if PY2 else operator.truediv) + __truediv__ = make_proxy_method(operator.truediv) + __floordiv__ = make_proxy_method(operator.floordiv) + __mod__ = make_proxy_method(operator.mod) + __divmod__ = make_proxy_method(divmod) + __pow__ = make_proxy_method(pow) + __lshift__ = make_proxy_method(operator.lshift) + __rshift__ = make_proxy_method(operator.rshift) + __and__ = make_proxy_method(operator.and_) + __xor__ = make_proxy_method(operator.xor) + __or__ = make_proxy_method(operator.or_) + + def __radd__(self, other): + return other + self.__wrapped__ + + def __rsub__(self, other): + return other - self.__wrapped__ + + def __rmul__(self, other): + return other * self.__wrapped__ + + def __rdiv__(self, other): + return operator.div(other, self.__wrapped__) + + def __rtruediv__(self, other): + return operator.truediv(other, self.__wrapped__) + + def __rfloordiv__(self, other): + return other // self.__wrapped__ + + def __rmod__(self, other): + return other % self.__wrapped__ + + def __rdivmod__(self, other): + return divmod(other, self.__wrapped__) + + def __rpow__(self, other, *args): + return pow(other, self.__wrapped__, *args) + + def __rlshift__(self, other): + return other << self.__wrapped__ + + def __rrshift__(self, other): + return other >> self.__wrapped__ + + def __rand__(self, other): + return other & self.__wrapped__ + + def __rxor__(self, other): + return other ^ self.__wrapped__ + + def __ror__(self, other): + return other | self.__wrapped__ + + __iadd__ = make_proxy_method(operator.iadd) + __isub__ = make_proxy_method(operator.isub) + __imul__ = make_proxy_method(operator.imul) + __idiv__ = make_proxy_method(operator.idiv if PY2 else operator.itruediv) + __itruediv__ = make_proxy_method(operator.itruediv) + __ifloordiv__ = make_proxy_method(operator.ifloordiv) + __imod__ = make_proxy_method(operator.imod) + __ipow__ = make_proxy_method(operator.ipow) + __ilshift__ = make_proxy_method(operator.ilshift) + __irshift__ = make_proxy_method(operator.irshift) + __iand__ = make_proxy_method(operator.iand) + __ixor__ = make_proxy_method(operator.ixor) + __ior__ = make_proxy_method(operator.ior) + __neg__ = make_proxy_method(operator.neg) + __pos__ = make_proxy_method(operator.pos) + __abs__ = make_proxy_method(operator.abs) + __invert__ = make_proxy_method(operator.invert) + + __int__ = make_proxy_method(int) + + if PY2: + __long__ = make_proxy_method(long) # noqa + + __float__ = make_proxy_method(float) + __oct__ = make_proxy_method(oct) + __hex__ = make_proxy_method(hex) + __index__ = make_proxy_method(operator.index) + __len__ = make_proxy_method(len) + __contains__ = make_proxy_method(operator.contains) + __getitem__ = make_proxy_method(operator.getitem) + __setitem__ = make_proxy_method(operator.setitem) + __delitem__ = make_proxy_method(operator.delitem) + + if PY2: + __getslice__ = make_proxy_method(operator.getslice) + __setslice__ = make_proxy_method(operator.setslice) + __delslice__ = make_proxy_method(operator.delslice) + + def __enter__(self): + return self.__wrapped__.__enter__() + + def __exit__(self, *args, **kwargs): + return self.__wrapped__.__exit__(*args, **kwargs) + + __iter__ = make_proxy_method(iter) + + def __call__(self, *args, **kwargs): + return self.__wrapped__(*args, **kwargs) + + def __reduce__(self): + return identity, (self.__wrapped__,) + + def __reduce_ex__(self, protocol): + return identity, (self.__wrapped__,) diff --git a/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/slots.py b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/slots.py new file mode 100644 index 0000000..efb08db --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/slots.py @@ -0,0 +1,414 @@ +import operator + +from .compat import PY2 +from .compat import PY3 +from .compat import with_metaclass +from .utils import identity + + +class _ProxyMethods(object): + # We use properties to override the values of __module__ and + # __doc__. If we add these in ObjectProxy, the derived class + # __dict__ will still be setup to have string variants of these + # attributes and the rules of descriptors means that they appear to + # take precedence over the properties in the base class. To avoid + # that, we copy the properties into the derived class type itself + # via a meta class. In that way the properties will always take + # precedence. + + @property + def __module__(self): + return self.__wrapped__.__module__ + + @__module__.setter + def __module__(self, value): + self.__wrapped__.__module__ = value + + @property + def __doc__(self): + return self.__wrapped__.__doc__ + + @__doc__.setter + def __doc__(self, value): + self.__wrapped__.__doc__ = value + + # We similar use a property for __dict__. We need __dict__ to be + # explicit to ensure that vars() works as expected. + + @property + def __dict__(self): + return self.__wrapped__.__dict__ + + # Need to also propagate the special __weakref__ attribute for case + # where decorating classes which will define this. If do not define + # it and use a function like inspect.getmembers() on a decorator + # class it will fail. This can't be in the derived classes. + + @property + def __weakref__(self): + return self.__wrapped__.__weakref__ + + +class _ProxyMetaType(type): + def __new__(cls, name, bases, dictionary): + # Copy our special properties into the class so that they + # always take precedence over attributes of the same name added + # during construction of a derived class. This is to save + # duplicating the implementation for them in all derived classes. + + dictionary.update(vars(_ProxyMethods)) + + return type.__new__(cls, name, bases, dictionary) + + +class Proxy(with_metaclass(_ProxyMetaType)): + """ + A proxy implementation in pure Python, using slots. You can subclass this to add + local methods or attributes, or enable __dict__. + + The most important internals: + + * ``__factory__`` is the callback that "materializes" the object we proxy to. + * ``__target__`` will contain the object we proxy to, once it's "materialized". + * ``__wrapped__`` is a property that does either: + + * return ``__target__`` if it's set. + * calls ``__factory__``, saves result to ``__target__`` and returns said result. + """ + + __slots__ = '__target__', '__factory__' + + def __init__(self, factory): + object.__setattr__(self, '__factory__', factory) + + @property + def __wrapped__(self, __getattr__=object.__getattribute__, __setattr__=object.__setattr__, + __delattr__=object.__delattr__): + try: + return __getattr__(self, '__target__') + except AttributeError: + try: + factory = __getattr__(self, '__factory__') + except AttributeError: + raise ValueError("Proxy hasn't been initiated: __factory__ is missing.") + target = factory() + __setattr__(self, '__target__', target) + return target + + @__wrapped__.deleter + def __wrapped__(self, __delattr__=object.__delattr__): + __delattr__(self, '__target__') + + @__wrapped__.setter + def __wrapped__(self, target, __setattr__=object.__setattr__): + __setattr__(self, '__target__', target) + + @property + def __name__(self): + return self.__wrapped__.__name__ + + @__name__.setter + def __name__(self, value): + self.__wrapped__.__name__ = value + + @property + def __class__(self): + return self.__wrapped__.__class__ + + @__class__.setter # noqa + def __class__(self, value): + self.__wrapped__.__class__ = value + + @property + def __annotations__(self): + return self.__wrapped__.__anotations__ + + @__annotations__.setter + def __annotations__(self, value): + self.__wrapped__.__annotations__ = value + + def __dir__(self): + return dir(self.__wrapped__) + + def __str__(self): + return str(self.__wrapped__) + + if PY3: + def __bytes__(self): + return bytes(self.__wrapped__) + + def __repr__(self, __getattr__=object.__getattribute__): + try: + target = __getattr__(self, '__target__') + except AttributeError: + return '<{} at 0x{:x} with factory {!r}>'.format( + type(self).__name__, id(self), + self.__factory__ + ) + else: + return '<{} at 0x{:x} wrapping {!r} at 0x{:x} with factory {!r}>'.format( + type(self).__name__, id(self), + target, id(target), + self.__factory__ + ) + + def __reversed__(self): + return reversed(self.__wrapped__) + + if PY3: + def __round__(self): + return round(self.__wrapped__) + + def __lt__(self, other): + return self.__wrapped__ < other + + def __le__(self, other): + return self.__wrapped__ <= other + + def __eq__(self, other): + return self.__wrapped__ == other + + def __ne__(self, other): + return self.__wrapped__ != other + + def __gt__(self, other): + return self.__wrapped__ > other + + def __ge__(self, other): + return self.__wrapped__ >= other + + def __hash__(self): + return hash(self.__wrapped__) + + def __nonzero__(self): + return bool(self.__wrapped__) + + def __bool__(self): + return bool(self.__wrapped__) + + def __setattr__(self, name, value, __setattr__=object.__setattr__): + if hasattr(type(self), name): + __setattr__(self, name, value) + else: + setattr(self.__wrapped__, name, value) + + def __getattr__(self, name): + if name in ('__wrapped__', '__factory__'): + raise AttributeError(name) + else: + return getattr(self.__wrapped__, name) + + def __delattr__(self, name, __delattr__=object.__delattr__): + if hasattr(type(self), name): + __delattr__(self, name) + else: + delattr(self.__wrapped__, name) + + def __add__(self, other): + return self.__wrapped__ + other + + def __sub__(self, other): + return self.__wrapped__ - other + + def __mul__(self, other): + return self.__wrapped__ * other + + def __div__(self, other): + return operator.div(self.__wrapped__, other) + + def __truediv__(self, other): + return operator.truediv(self.__wrapped__, other) + + def __floordiv__(self, other): + return self.__wrapped__ // other + + def __mod__(self, other): + return self.__wrapped__ % other + + def __divmod__(self, other): + return divmod(self.__wrapped__, other) + + def __pow__(self, other, *args): + return pow(self.__wrapped__, other, *args) + + def __lshift__(self, other): + return self.__wrapped__ << other + + def __rshift__(self, other): + return self.__wrapped__ >> other + + def __and__(self, other): + return self.__wrapped__ & other + + def __xor__(self, other): + return self.__wrapped__ ^ other + + def __or__(self, other): + return self.__wrapped__ | other + + def __radd__(self, other): + return other + self.__wrapped__ + + def __rsub__(self, other): + return other - self.__wrapped__ + + def __rmul__(self, other): + return other * self.__wrapped__ + + def __rdiv__(self, other): + return operator.div(other, self.__wrapped__) + + def __rtruediv__(self, other): + return operator.truediv(other, self.__wrapped__) + + def __rfloordiv__(self, other): + return other // self.__wrapped__ + + def __rmod__(self, other): + return other % self.__wrapped__ + + def __rdivmod__(self, other): + return divmod(other, self.__wrapped__) + + def __rpow__(self, other, *args): + return pow(other, self.__wrapped__, *args) + + def __rlshift__(self, other): + return other << self.__wrapped__ + + def __rrshift__(self, other): + return other >> self.__wrapped__ + + def __rand__(self, other): + return other & self.__wrapped__ + + def __rxor__(self, other): + return other ^ self.__wrapped__ + + def __ror__(self, other): + return other | self.__wrapped__ + + def __iadd__(self, other): + self.__wrapped__ += other + return self + + def __isub__(self, other): + self.__wrapped__ -= other + return self + + def __imul__(self, other): + self.__wrapped__ *= other + return self + + def __idiv__(self, other): + self.__wrapped__ = operator.idiv(self.__wrapped__, other) + return self + + def __itruediv__(self, other): + self.__wrapped__ = operator.itruediv(self.__wrapped__, other) + return self + + def __ifloordiv__(self, other): + self.__wrapped__ //= other + return self + + def __imod__(self, other): + self.__wrapped__ %= other + return self + + def __ipow__(self, other): + self.__wrapped__ **= other + return self + + def __ilshift__(self, other): + self.__wrapped__ <<= other + return self + + def __irshift__(self, other): + self.__wrapped__ >>= other + return self + + def __iand__(self, other): + self.__wrapped__ &= other + return self + + def __ixor__(self, other): + self.__wrapped__ ^= other + return self + + def __ior__(self, other): + self.__wrapped__ |= other + return self + + def __neg__(self): + return -self.__wrapped__ + + def __pos__(self): + return +self.__wrapped__ + + def __abs__(self): + return abs(self.__wrapped__) + + def __invert__(self): + return ~self.__wrapped__ + + def __int__(self): + return int(self.__wrapped__) + + if PY2: + def __long__(self): + return long(self.__wrapped__) # noqa + + def __float__(self): + return float(self.__wrapped__) + + def __oct__(self): + return oct(self.__wrapped__) + + def __hex__(self): + return hex(self.__wrapped__) + + def __index__(self): + return operator.index(self.__wrapped__) + + def __len__(self): + return len(self.__wrapped__) + + def __contains__(self, value): + return value in self.__wrapped__ + + def __getitem__(self, key): + return self.__wrapped__[key] + + def __setitem__(self, key, value): + self.__wrapped__[key] = value + + def __delitem__(self, key): + del self.__wrapped__[key] + + def __getslice__(self, i, j): + return self.__wrapped__[i:j] + + def __setslice__(self, i, j, value): + self.__wrapped__[i:j] = value + + def __delslice__(self, i, j): + del self.__wrapped__[i:j] + + def __enter__(self): + return self.__wrapped__.__enter__() + + def __exit__(self, *args, **kwargs): + return self.__wrapped__.__exit__(*args, **kwargs) + + def __iter__(self): + return iter(self.__wrapped__) + + def __call__(self, *args, **kwargs): + return self.__wrapped__(*args, **kwargs) + + def __reduce__(self): + return identity, (self.__wrapped__,) + + def __reduce_ex__(self, protocol): + return identity, (self.__wrapped__,) diff --git a/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/utils.py b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/utils.py new file mode 100644 index 0000000..ceb3050 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/lazy_object_proxy/utils.py @@ -0,0 +1,13 @@ +def identity(obj): + return obj + + +class cached_property(object): + def __init__(self, func): + self.func = func + + def __get__(self, obj, cls): + if obj is None: + return self + value = obj.__dict__[self.func.__name__] = self.func(obj) + return value diff --git a/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/DESCRIPTION.rst b/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..de61068 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/DESCRIPTION.rst @@ -0,0 +1,152 @@ +McCabe complexity checker +========================= + +Ned's script to check McCabe complexity. + +This module provides a plugin for ``flake8``, the Python code checker. + + +Installation +------------ + +You can install, upgrade, uninstall ``mccabe`` with these commands:: + + $ pip install mccabe + $ pip install --upgrade mccabe + $ pip uninstall mccabe + + +Standalone script +----------------- + +The complexity checker can be used directly:: + + $ python -m mccabe --min 5 mccabe.py + ("185:1: 'PathGraphingAstVisitor.visitIf'", 5) + ("71:1: 'PathGraph.to_dot'", 5) + ("245:1: 'McCabeChecker.run'", 5) + ("283:1: 'main'", 7) + ("203:1: 'PathGraphingAstVisitor.visitTryExcept'", 5) + ("257:1: 'get_code_complexity'", 5) + + +Plugin for Flake8 +----------------- + +When both ``flake8 2.0`` and ``mccabe`` are installed, the plugin is +available in ``flake8``:: + + $ flake8 --version + 2.0 (pep8: 1.4.2, pyflakes: 0.6.1, mccabe: 0.2) + +By default the plugin is disabled. Use the ``--max-complexity`` switch to +enable it. It will emit a warning if the McCabe complexity of a function is +higher that the value:: + + $ flake8 --max-complexity 10 coolproject + ... + coolproject/mod.py:1204:1: C901 'CoolFactory.prepare' is too complex (14) + +This feature is quite useful to detect over-complex code. According to McCabe, +anything that goes beyond 10 is too complex. + + +Links +----- + +* Feedback and ideas: http://mail.python.org/mailman/listinfo/code-quality + +* Cyclomatic complexity: http://en.wikipedia.org/wiki/Cyclomatic_complexity. + +* Ned Batchelder's script: + http://nedbatchelder.com/blog/200803/python_code_complexity_microtool.html + + +Changes +------- + +0.6.1 - 2017-01-26 +`````````````````` + +* Fix signature for ``PathGraphingAstVisitor.default`` to match the signature + for ``ASTVisitor`` + +0.6.0 - 2017-01-23 +`````````````````` + +* Add support for Python 3.6 + +* Fix handling for missing statement types + +0.5.3 - 2016-12-14 +`````````````````` + +* Report actual column number of violation instead of the start of the line + +0.5.2 - 2016-07-31 +`````````````````` + +* When opening files ourselves, make sure we always name the file variable + +0.5.1 - 2016-07-28 +`````````````````` + +* Set default maximum complexity to -1 on the class itself + +0.5.0 - 2016-05-30 +`````````````````` + +* PyCon 2016 PDX release + +* Add support for Flake8 3.0 + +0.4.0 - 2016-01-27 +`````````````````` + +* Stop testing on Python 3.2 + +* Add support for async/await keywords on Python 3.5 from PEP 0492 + +0.3.1 - 2015-06-14 +`````````````````` + +* Include ``test_mccabe.py`` in releases. + +* Always coerce the ``max_complexity`` value from Flake8's entry-point to an + integer. + +0.3 - 2014-12-17 +```````````````` + +* Computation was wrong: the mccabe complexity starts at 1, not 2. + +* The ``max-complexity`` value is now inclusive. E.g.: if the + value is 10 and the reported complexity is 10, then it passes. + +* Add tests. + + +0.2.1 - 2013-04-03 +`````````````````` + +* Do not require ``setuptools`` in setup.py. It works around an issue + with ``pip`` and Python 3. + + +0.2 - 2013-02-22 +```````````````` + +* Rename project to ``mccabe``. + +* Provide ``flake8.extension`` setuptools entry point. + +* Read ``max-complexity`` from the configuration file. + +* Rename argument ``min_complexity`` to ``threshold``. + + +0.1 - 2013-02-11 +```````````````` +* First release + + diff --git a/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/INSTALLER b/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/METADATA b/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/METADATA new file mode 100644 index 0000000..f22645f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/METADATA @@ -0,0 +1,178 @@ +Metadata-Version: 2.0 +Name: mccabe +Version: 0.6.1 +Summary: McCabe checker, plugin for flake8 +Home-page: https://github.com/pycqa/mccabe +Author: Ian Cordasco +Author-email: graffatcolmingov@gmail.com +License: Expat license +Keywords: flake8 mccabe +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Software Development :: Quality Assurance + +McCabe complexity checker +========================= + +Ned's script to check McCabe complexity. + +This module provides a plugin for ``flake8``, the Python code checker. + + +Installation +------------ + +You can install, upgrade, uninstall ``mccabe`` with these commands:: + + $ pip install mccabe + $ pip install --upgrade mccabe + $ pip uninstall mccabe + + +Standalone script +----------------- + +The complexity checker can be used directly:: + + $ python -m mccabe --min 5 mccabe.py + ("185:1: 'PathGraphingAstVisitor.visitIf'", 5) + ("71:1: 'PathGraph.to_dot'", 5) + ("245:1: 'McCabeChecker.run'", 5) + ("283:1: 'main'", 7) + ("203:1: 'PathGraphingAstVisitor.visitTryExcept'", 5) + ("257:1: 'get_code_complexity'", 5) + + +Plugin for Flake8 +----------------- + +When both ``flake8 2.0`` and ``mccabe`` are installed, the plugin is +available in ``flake8``:: + + $ flake8 --version + 2.0 (pep8: 1.4.2, pyflakes: 0.6.1, mccabe: 0.2) + +By default the plugin is disabled. Use the ``--max-complexity`` switch to +enable it. It will emit a warning if the McCabe complexity of a function is +higher that the value:: + + $ flake8 --max-complexity 10 coolproject + ... + coolproject/mod.py:1204:1: C901 'CoolFactory.prepare' is too complex (14) + +This feature is quite useful to detect over-complex code. According to McCabe, +anything that goes beyond 10 is too complex. + + +Links +----- + +* Feedback and ideas: http://mail.python.org/mailman/listinfo/code-quality + +* Cyclomatic complexity: http://en.wikipedia.org/wiki/Cyclomatic_complexity. + +* Ned Batchelder's script: + http://nedbatchelder.com/blog/200803/python_code_complexity_microtool.html + + +Changes +------- + +0.6.1 - 2017-01-26 +`````````````````` + +* Fix signature for ``PathGraphingAstVisitor.default`` to match the signature + for ``ASTVisitor`` + +0.6.0 - 2017-01-23 +`````````````````` + +* Add support for Python 3.6 + +* Fix handling for missing statement types + +0.5.3 - 2016-12-14 +`````````````````` + +* Report actual column number of violation instead of the start of the line + +0.5.2 - 2016-07-31 +`````````````````` + +* When opening files ourselves, make sure we always name the file variable + +0.5.1 - 2016-07-28 +`````````````````` + +* Set default maximum complexity to -1 on the class itself + +0.5.0 - 2016-05-30 +`````````````````` + +* PyCon 2016 PDX release + +* Add support for Flake8 3.0 + +0.4.0 - 2016-01-27 +`````````````````` + +* Stop testing on Python 3.2 + +* Add support for async/await keywords on Python 3.5 from PEP 0492 + +0.3.1 - 2015-06-14 +`````````````````` + +* Include ``test_mccabe.py`` in releases. + +* Always coerce the ``max_complexity`` value from Flake8's entry-point to an + integer. + +0.3 - 2014-12-17 +```````````````` + +* Computation was wrong: the mccabe complexity starts at 1, not 2. + +* The ``max-complexity`` value is now inclusive. E.g.: if the + value is 10 and the reported complexity is 10, then it passes. + +* Add tests. + + +0.2.1 - 2013-04-03 +`````````````````` + +* Do not require ``setuptools`` in setup.py. It works around an issue + with ``pip`` and Python 3. + + +0.2 - 2013-02-22 +```````````````` + +* Rename project to ``mccabe``. + +* Provide ``flake8.extension`` setuptools entry point. + +* Read ``max-complexity`` from the configuration file. + +* Rename argument ``min_complexity`` to ``threshold``. + + +0.1 - 2013-02-11 +```````````````` +* First release + + diff --git a/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/RECORD b/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/RECORD new file mode 100644 index 0000000..5ac5827 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/RECORD @@ -0,0 +1,10 @@ +__pycache__/mccabe.cpython-37.pyc,, +mccabe-0.6.1.dist-info/DESCRIPTION.rst,sha256=lGHJ-Y3IviuP3DRqLL_TXPUr3wJ2GZ8XJkAV6ve3O58,3302 +mccabe-0.6.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +mccabe-0.6.1.dist-info/METADATA,sha256=jawnTfTVrlzBSmeI-cTXSRIwIlijODtZdj-padBqIv0,4324 +mccabe-0.6.1.dist-info/RECORD,, +mccabe-0.6.1.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 +mccabe-0.6.1.dist-info/entry_points.txt,sha256=N2NH182GXTUyTm8r8XMgadb9C-CRa5dUr1k8OC91uGE,47 +mccabe-0.6.1.dist-info/metadata.json,sha256=e508OR4t6_G7h7eO2C6NHlHQqVpPZZH1_DlAPrVECYM,1218 +mccabe-0.6.1.dist-info/top_level.txt,sha256=21cXuqZE-lpcfAqqANvX9EjI1ED1p8zcViv064u3RKA,7 +mccabe.py,sha256=XPMywdQshG_5nSjckb-OzNqnCQuXQvy3FTClspKwGQA,10693 diff --git a/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/WHEEL b/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/WHEEL new file mode 100644 index 0000000..8b6dd1b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.29.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/entry_points.txt b/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/entry_points.txt new file mode 100644 index 0000000..cc6645b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[flake8.extension] +C90 = mccabe:McCabeChecker + diff --git a/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/metadata.json b/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/metadata.json new file mode 100644 index 0000000..ae04d8f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/metadata.json @@ -0,0 +1 @@ +{"classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Quality Assurance"], "extensions": {"python.details": {"contacts": [{"email": "graffatcolmingov@gmail.com", "name": "Ian Cordasco", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/pycqa/mccabe"}}, "python.exports": {"flake8.extension": {"C90": "mccabe:McCabeChecker"}}}, "generator": "bdist_wheel (0.29.0)", "keywords": ["flake8", "mccabe"], "license": "Expat license", "metadata_version": "2.0", "name": "mccabe", "summary": "McCabe checker, plugin for flake8", "test_requires": [{"requires": ["pytest"]}], "version": "0.6.1"} \ No newline at end of file diff --git a/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/top_level.txt b/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/top_level.txt new file mode 100644 index 0000000..8831b36 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/mccabe-0.6.1.dist-info/top_level.txt @@ -0,0 +1 @@ +mccabe diff --git a/Display/.venv/lib/python3.7/site-packages/mccabe.py b/Display/.venv/lib/python3.7/site-packages/mccabe.py new file mode 100644 index 0000000..c0cda75 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/mccabe.py @@ -0,0 +1,347 @@ +""" Meager code path measurement tool. + Ned Batchelder + http://nedbatchelder.com/blog/200803/python_code_complexity_microtool.html + MIT License. +""" +from __future__ import with_statement + +import optparse +import sys +import tokenize + +from collections import defaultdict +try: + import ast + from ast import iter_child_nodes +except ImportError: # Python 2.5 + from flake8.util import ast, iter_child_nodes + +__version__ = '0.6.1' + + +class ASTVisitor(object): + """Performs a depth-first walk of the AST.""" + + def __init__(self): + self.node = None + self._cache = {} + + def default(self, node, *args): + for child in iter_child_nodes(node): + self.dispatch(child, *args) + + def dispatch(self, node, *args): + self.node = node + klass = node.__class__ + meth = self._cache.get(klass) + if meth is None: + className = klass.__name__ + meth = getattr(self.visitor, 'visit' + className, self.default) + self._cache[klass] = meth + return meth(node, *args) + + def preorder(self, tree, visitor, *args): + """Do preorder walk of tree using visitor""" + self.visitor = visitor + visitor.visit = self.dispatch + self.dispatch(tree, *args) # XXX *args make sense? + + +class PathNode(object): + def __init__(self, name, look="circle"): + self.name = name + self.look = look + + def to_dot(self): + print('node [shape=%s,label="%s"] %d;' % ( + self.look, self.name, self.dot_id())) + + def dot_id(self): + return id(self) + + +class PathGraph(object): + def __init__(self, name, entity, lineno, column=0): + self.name = name + self.entity = entity + self.lineno = lineno + self.column = column + self.nodes = defaultdict(list) + + def connect(self, n1, n2): + self.nodes[n1].append(n2) + # Ensure that the destination node is always counted. + self.nodes[n2] = [] + + def to_dot(self): + print('subgraph {') + for node in self.nodes: + node.to_dot() + for node, nexts in self.nodes.items(): + for next in nexts: + print('%s -- %s;' % (node.dot_id(), next.dot_id())) + print('}') + + def complexity(self): + """ Return the McCabe complexity for the graph. + V-E+2 + """ + num_edges = sum([len(n) for n in self.nodes.values()]) + num_nodes = len(self.nodes) + return num_edges - num_nodes + 2 + + +class PathGraphingAstVisitor(ASTVisitor): + """ A visitor for a parsed Abstract Syntax Tree which finds executable + statements. + """ + + def __init__(self): + super(PathGraphingAstVisitor, self).__init__() + self.classname = "" + self.graphs = {} + self.reset() + + def reset(self): + self.graph = None + self.tail = None + + def dispatch_list(self, node_list): + for node in node_list: + self.dispatch(node) + + def visitFunctionDef(self, node): + + if self.classname: + entity = '%s%s' % (self.classname, node.name) + else: + entity = node.name + + name = '%d:%d: %r' % (node.lineno, node.col_offset, entity) + + if self.graph is not None: + # closure + pathnode = self.appendPathNode(name) + self.tail = pathnode + self.dispatch_list(node.body) + bottom = PathNode("", look='point') + self.graph.connect(self.tail, bottom) + self.graph.connect(pathnode, bottom) + self.tail = bottom + else: + self.graph = PathGraph(name, entity, node.lineno, node.col_offset) + pathnode = PathNode(name) + self.tail = pathnode + self.dispatch_list(node.body) + self.graphs["%s%s" % (self.classname, node.name)] = self.graph + self.reset() + + visitAsyncFunctionDef = visitFunctionDef + + def visitClassDef(self, node): + old_classname = self.classname + self.classname += node.name + "." + self.dispatch_list(node.body) + self.classname = old_classname + + def appendPathNode(self, name): + if not self.tail: + return + pathnode = PathNode(name) + self.graph.connect(self.tail, pathnode) + self.tail = pathnode + return pathnode + + def visitSimpleStatement(self, node): + if node.lineno is None: + lineno = 0 + else: + lineno = node.lineno + name = "Stmt %d" % lineno + self.appendPathNode(name) + + def default(self, node, *args): + if isinstance(node, ast.stmt): + self.visitSimpleStatement(node) + else: + super(PathGraphingAstVisitor, self).default(node, *args) + + def visitLoop(self, node): + name = "Loop %d" % node.lineno + self._subgraph(node, name) + + visitAsyncFor = visitFor = visitWhile = visitLoop + + def visitIf(self, node): + name = "If %d" % node.lineno + self._subgraph(node, name) + + def _subgraph(self, node, name, extra_blocks=()): + """create the subgraphs representing any `if` and `for` statements""" + if self.graph is None: + # global loop + self.graph = PathGraph(name, name, node.lineno, node.col_offset) + pathnode = PathNode(name) + self._subgraph_parse(node, pathnode, extra_blocks) + self.graphs["%s%s" % (self.classname, name)] = self.graph + self.reset() + else: + pathnode = self.appendPathNode(name) + self._subgraph_parse(node, pathnode, extra_blocks) + + def _subgraph_parse(self, node, pathnode, extra_blocks): + """parse the body and any `else` block of `if` and `for` statements""" + loose_ends = [] + self.tail = pathnode + self.dispatch_list(node.body) + loose_ends.append(self.tail) + for extra in extra_blocks: + self.tail = pathnode + self.dispatch_list(extra.body) + loose_ends.append(self.tail) + if node.orelse: + self.tail = pathnode + self.dispatch_list(node.orelse) + loose_ends.append(self.tail) + else: + loose_ends.append(pathnode) + if pathnode: + bottom = PathNode("", look='point') + for le in loose_ends: + self.graph.connect(le, bottom) + self.tail = bottom + + def visitTryExcept(self, node): + name = "TryExcept %d" % node.lineno + self._subgraph(node, name, extra_blocks=node.handlers) + + visitTry = visitTryExcept + + def visitWith(self, node): + name = "With %d" % node.lineno + self.appendPathNode(name) + self.dispatch_list(node.body) + + visitAsyncWith = visitWith + + +class McCabeChecker(object): + """McCabe cyclomatic complexity checker.""" + name = 'mccabe' + version = __version__ + _code = 'C901' + _error_tmpl = "C901 %r is too complex (%d)" + max_complexity = -1 + + def __init__(self, tree, filename): + self.tree = tree + + @classmethod + def add_options(cls, parser): + flag = '--max-complexity' + kwargs = { + 'default': -1, + 'action': 'store', + 'type': 'int', + 'help': 'McCabe complexity threshold', + 'parse_from_config': 'True', + } + config_opts = getattr(parser, 'config_options', None) + if isinstance(config_opts, list): + # Flake8 2.x + kwargs.pop('parse_from_config') + parser.add_option(flag, **kwargs) + parser.config_options.append('max-complexity') + else: + parser.add_option(flag, **kwargs) + + @classmethod + def parse_options(cls, options): + cls.max_complexity = int(options.max_complexity) + + def run(self): + if self.max_complexity < 0: + return + visitor = PathGraphingAstVisitor() + visitor.preorder(self.tree, visitor) + for graph in visitor.graphs.values(): + if graph.complexity() > self.max_complexity: + text = self._error_tmpl % (graph.entity, graph.complexity()) + yield graph.lineno, graph.column, text, type(self) + + +def get_code_complexity(code, threshold=7, filename='stdin'): + try: + tree = compile(code, filename, "exec", ast.PyCF_ONLY_AST) + except SyntaxError: + e = sys.exc_info()[1] + sys.stderr.write("Unable to parse %s: %s\n" % (filename, e)) + return 0 + + complx = [] + McCabeChecker.max_complexity = threshold + for lineno, offset, text, check in McCabeChecker(tree, filename).run(): + complx.append('%s:%d:1: %s' % (filename, lineno, text)) + + if len(complx) == 0: + return 0 + print('\n'.join(complx)) + return len(complx) + + +def get_module_complexity(module_path, threshold=7): + """Returns the complexity of a module""" + with open(module_path, "rU") as mod: + code = mod.read() + return get_code_complexity(code, threshold, filename=module_path) + + +def _read(filename): + if (2, 5) < sys.version_info < (3, 0): + with open(filename, 'rU') as f: + return f.read() + elif (3, 0) <= sys.version_info < (4, 0): + """Read the source code.""" + try: + with open(filename, 'rb') as f: + (encoding, _) = tokenize.detect_encoding(f.readline) + except (LookupError, SyntaxError, UnicodeError): + # Fall back if file encoding is improperly declared + with open(filename, encoding='latin-1') as f: + return f.read() + with open(filename, 'r', encoding=encoding) as f: + return f.read() + + +def main(argv=None): + if argv is None: + argv = sys.argv[1:] + opar = optparse.OptionParser() + opar.add_option("-d", "--dot", dest="dot", + help="output a graphviz dot file", action="store_true") + opar.add_option("-m", "--min", dest="threshold", + help="minimum complexity for output", type="int", + default=1) + + options, args = opar.parse_args(argv) + + code = _read(args[0]) + tree = compile(code, args[0], "exec", ast.PyCF_ONLY_AST) + visitor = PathGraphingAstVisitor() + visitor.preorder(tree, visitor) + + if options.dot: + print('graph {') + for graph in visitor.graphs.values(): + if (not options.threshold or + graph.complexity() >= options.threshold): + graph.to_dot() + print('}') + else: + for graph in visitor.graphs.values(): + if graph.complexity() >= options.threshold: + print(graph.name, graph.complexity()) + + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/Display/.venv/lib/python3.7/site-packages/paho/__init__.py b/Display/.venv/lib/python3.7/site-packages/paho/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Display/.venv/lib/python3.7/site-packages/paho/mqtt/__init__.py b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/__init__.py new file mode 100644 index 0000000..45b5470 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/__init__.py @@ -0,0 +1,5 @@ +__version__ = "1.5.0" + + +class MQTTException(Exception): + pass diff --git a/Display/.venv/lib/python3.7/site-packages/paho/mqtt/client.py b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/client.py new file mode 100644 index 0000000..ae2b511 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/client.py @@ -0,0 +1,3834 @@ +# Copyright (c) 2012-2019 Roger Light and others +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# and Eclipse Distribution License v1.0 which accompany this distribution. +# +# The Eclipse Public License is available at +# http://www.eclipse.org/legal/epl-v10.html +# and the Eclipse Distribution License is available at +# http://www.eclipse.org/org/documents/edl-v10.php. +# +# Contributors: +# Roger Light - initial API and implementation +# Ian Craggs - MQTT V5 support + +from .subscribeoptions import SubscribeOptions +from .reasoncodes import ReasonCodes +from .properties import Properties +from .matcher import MQTTMatcher +import logging +import hashlib +import string +import base64 +import uuid +import time +import threading +import sys +import struct +""" +This is an MQTT client module. MQTT is a lightweight pub/sub messaging +protocol that is easy to implement and suitable for low powered devices. +""" +import collections +import errno +import os +import platform +import select +import socket + +ssl = None +try: + import ssl +except ImportError: + pass + +socks = None +try: + import socks +except ImportError: + pass + +try: + # Python 3 + from urllib import request as urllib_dot_request + from urllib import parse as urllib_dot_parse +except ImportError: + # Python 2 + import urllib as urllib_dot_request + import urlparse as urllib_dot_parse + + +try: + # Use monotonic clock if available + time_func = time.monotonic +except AttributeError: + time_func = time.time + +try: + import dns.resolver +except ImportError: + HAVE_DNS = False +else: + HAVE_DNS = True + + +if platform.system() == 'Windows': + EAGAIN = errno.WSAEWOULDBLOCK +else: + EAGAIN = errno.EAGAIN + +MQTTv31 = 3 +MQTTv311 = 4 +MQTTv5 = 5 + +if sys.version_info[0] >= 3: + # define some alias for python2 compatibility + unicode = str + basestring = str + +# Message types +CONNECT = 0x10 +CONNACK = 0x20 +PUBLISH = 0x30 +PUBACK = 0x40 +PUBREC = 0x50 +PUBREL = 0x60 +PUBCOMP = 0x70 +SUBSCRIBE = 0x80 +SUBACK = 0x90 +UNSUBSCRIBE = 0xA0 +UNSUBACK = 0xB0 +PINGREQ = 0xC0 +PINGRESP = 0xD0 +DISCONNECT = 0xE0 +AUTH = 0xF0 + +# Log levels +MQTT_LOG_INFO = 0x01 +MQTT_LOG_NOTICE = 0x02 +MQTT_LOG_WARNING = 0x04 +MQTT_LOG_ERR = 0x08 +MQTT_LOG_DEBUG = 0x10 +LOGGING_LEVEL = { + MQTT_LOG_DEBUG: logging.DEBUG, + MQTT_LOG_INFO: logging.INFO, + MQTT_LOG_NOTICE: logging.INFO, # This has no direct equivalent level + MQTT_LOG_WARNING: logging.WARNING, + MQTT_LOG_ERR: logging.ERROR, +} + +# CONNACK codes +CONNACK_ACCEPTED = 0 +CONNACK_REFUSED_PROTOCOL_VERSION = 1 +CONNACK_REFUSED_IDENTIFIER_REJECTED = 2 +CONNACK_REFUSED_SERVER_UNAVAILABLE = 3 +CONNACK_REFUSED_BAD_USERNAME_PASSWORD = 4 +CONNACK_REFUSED_NOT_AUTHORIZED = 5 + +# Connection state +mqtt_cs_new = 0 +mqtt_cs_connected = 1 +mqtt_cs_disconnecting = 2 +mqtt_cs_connect_async = 3 + +# Message state +mqtt_ms_invalid = 0 +mqtt_ms_publish = 1 +mqtt_ms_wait_for_puback = 2 +mqtt_ms_wait_for_pubrec = 3 +mqtt_ms_resend_pubrel = 4 +mqtt_ms_wait_for_pubrel = 5 +mqtt_ms_resend_pubcomp = 6 +mqtt_ms_wait_for_pubcomp = 7 +mqtt_ms_send_pubrec = 8 +mqtt_ms_queued = 9 + +# Error values +MQTT_ERR_AGAIN = -1 +MQTT_ERR_SUCCESS = 0 +MQTT_ERR_NOMEM = 1 +MQTT_ERR_PROTOCOL = 2 +MQTT_ERR_INVAL = 3 +MQTT_ERR_NO_CONN = 4 +MQTT_ERR_CONN_REFUSED = 5 +MQTT_ERR_NOT_FOUND = 6 +MQTT_ERR_CONN_LOST = 7 +MQTT_ERR_TLS = 8 +MQTT_ERR_PAYLOAD_SIZE = 9 +MQTT_ERR_NOT_SUPPORTED = 10 +MQTT_ERR_AUTH = 11 +MQTT_ERR_ACL_DENIED = 12 +MQTT_ERR_UNKNOWN = 13 +MQTT_ERR_ERRNO = 14 +MQTT_ERR_QUEUE_SIZE = 15 + +MQTT_CLIENT = 0 +MQTT_BRIDGE = 1 + +# For MQTT V5, use the clean start flag only on the first successful connect +MQTT_CLEAN_START_FIRST_ONLY = 3 + +sockpair_data = b"0" + + +class WebsocketConnectionError(ValueError): + pass + + +class WouldBlockError(Exception): + pass + + +def error_string(mqtt_errno): + """Return the error string associated with an mqtt error number.""" + if mqtt_errno == MQTT_ERR_SUCCESS: + return "No error." + elif mqtt_errno == MQTT_ERR_NOMEM: + return "Out of memory." + elif mqtt_errno == MQTT_ERR_PROTOCOL: + return "A network protocol error occurred when communicating with the broker." + elif mqtt_errno == MQTT_ERR_INVAL: + return "Invalid function arguments provided." + elif mqtt_errno == MQTT_ERR_NO_CONN: + return "The client is not currently connected." + elif mqtt_errno == MQTT_ERR_CONN_REFUSED: + return "The connection was refused." + elif mqtt_errno == MQTT_ERR_NOT_FOUND: + return "Message not found (internal error)." + elif mqtt_errno == MQTT_ERR_CONN_LOST: + return "The connection was lost." + elif mqtt_errno == MQTT_ERR_TLS: + return "A TLS error occurred." + elif mqtt_errno == MQTT_ERR_PAYLOAD_SIZE: + return "Payload too large." + elif mqtt_errno == MQTT_ERR_NOT_SUPPORTED: + return "This feature is not supported." + elif mqtt_errno == MQTT_ERR_AUTH: + return "Authorisation failed." + elif mqtt_errno == MQTT_ERR_ACL_DENIED: + return "Access denied by ACL." + elif mqtt_errno == MQTT_ERR_UNKNOWN: + return "Unknown error." + elif mqtt_errno == MQTT_ERR_ERRNO: + return "Error defined by errno." + elif mqtt_errno == MQTT_ERR_QUEUE_SIZE: + return "Message queue full." + else: + return "Unknown error." + + +def connack_string(connack_code): + """Return the string associated with a CONNACK result.""" + if connack_code == CONNACK_ACCEPTED: + return "Connection Accepted." + elif connack_code == CONNACK_REFUSED_PROTOCOL_VERSION: + return "Connection Refused: unacceptable protocol version." + elif connack_code == CONNACK_REFUSED_IDENTIFIER_REJECTED: + return "Connection Refused: identifier rejected." + elif connack_code == CONNACK_REFUSED_SERVER_UNAVAILABLE: + return "Connection Refused: broker unavailable." + elif connack_code == CONNACK_REFUSED_BAD_USERNAME_PASSWORD: + return "Connection Refused: bad user name or password." + elif connack_code == CONNACK_REFUSED_NOT_AUTHORIZED: + return "Connection Refused: not authorised." + else: + return "Connection Refused: unknown reason." + + +def base62(num, base=string.digits + string.ascii_letters, padding=1): + """Convert a number to base-62 representation.""" + assert num >= 0 + digits = [] + while num: + num, rest = divmod(num, 62) + digits.append(base[rest]) + digits.extend(base[0] for _ in range(len(digits), padding)) + return ''.join(reversed(digits)) + + +def topic_matches_sub(sub, topic): + """Check whether a topic matches a subscription. + + For example: + + foo/bar would match the subscription foo/# or +/bar + non/matching would not match the subscription non/+/+ + """ + matcher = MQTTMatcher() + matcher[sub] = True + try: + next(matcher.iter_match(topic)) + return True + except StopIteration: + return False + + +def _socketpair_compat(): + """TCP/IP socketpair including Windows support""" + listensock = socket.socket( + socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_IP) + listensock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + listensock.bind(("127.0.0.1", 0)) + listensock.listen(1) + + iface, port = listensock.getsockname() + sock1 = socket.socket( + socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_IP) + sock1.setblocking(0) + try: + sock1.connect(("127.0.0.1", port)) + except socket.error as err: + if err.errno != errno.EINPROGRESS and err.errno != errno.EWOULDBLOCK and err.errno != EAGAIN: + raise + sock2, address = listensock.accept() + sock2.setblocking(0) + listensock.close() + return (sock1, sock2) + + +class MQTTMessageInfo(object): + """This is a class returned from Client.publish() and can be used to find + out the mid of the message that was published, and to determine whether the + message has been published, and/or wait until it is published. + """ + + __slots__ = 'mid', '_published', '_condition', 'rc', '_iterpos' + + def __init__(self, mid): + self.mid = mid + self._published = False + self._condition = threading.Condition() + self.rc = 0 + self._iterpos = 0 + + def __str__(self): + return str((self.rc, self.mid)) + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + return self.next() + + def next(self): + if self._iterpos == 0: + self._iterpos = 1 + return self.rc + elif self._iterpos == 1: + self._iterpos = 2 + return self.mid + else: + raise StopIteration + + def __getitem__(self, index): + if index == 0: + return self.rc + elif index == 1: + return self.mid + else: + raise IndexError("index out of range") + + def _set_as_published(self): + with self._condition: + self._published = True + self._condition.notify() + + def wait_for_publish(self): + """Block until the message associated with this object is published.""" + if self.rc == MQTT_ERR_QUEUE_SIZE: + raise ValueError('Message is not queued due to ERR_QUEUE_SIZE') + with self._condition: + while not self._published: + self._condition.wait() + + def is_published(self): + """Returns True if the message associated with this object has been + published, else returns False.""" + if self.rc == MQTT_ERR_QUEUE_SIZE: + raise ValueError('Message is not queued due to ERR_QUEUE_SIZE') + with self._condition: + return self._published + + +class MQTTMessage(object): + """ This is a class that describes an incoming or outgoing message. It is + passed to the on_message callback as the message parameter. + + Members: + + topic : String/bytes. topic that the message was published on. + payload : String/bytes the message payload. + qos : Integer. The message Quality of Service 0, 1 or 2. + retain : Boolean. If true, the message is a retained message and not fresh. + mid : Integer. The message id. + properties: Properties class. In MQTT v5.0, the properties associated with the message. + + On Python 3, topic must be bytes. + """ + + __slots__ = 'timestamp', 'state', 'dup', 'mid', '_topic', 'payload', 'qos', 'retain', 'info', 'properties' + + def __init__(self, mid=0, topic=b""): + self.timestamp = 0 + self.state = mqtt_ms_invalid + self.dup = False + self.mid = mid + self._topic = topic + self.payload = b"" + self.qos = 0 + self.retain = False + self.info = MQTTMessageInfo(mid) + + def __eq__(self, other): + """Override the default Equals behavior""" + if isinstance(other, self.__class__): + return self.mid == other.mid + return False + + def __ne__(self, other): + """Define a non-equality test""" + return not self.__eq__(other) + + @property + def topic(self): + return self._topic.decode('utf-8') + + @topic.setter + def topic(self, value): + self._topic = value + + +class Client(object): + """MQTT version 3.1/3.1.1/5.0 client class. + + This is the main class for use communicating with an MQTT broker. + + General usage flow: + + * Use connect()/connect_async() to connect to a broker + * Call loop() frequently to maintain network traffic flow with the broker + * Or use loop_start() to set a thread running to call loop() for you. + * Or use loop_forever() to handle calling loop() for you in a blocking + * function. + * Use subscribe() to subscribe to a topic and receive messages + * Use publish() to send messages + * Use disconnect() to disconnect from the broker + + Data returned from the broker is made available with the use of callback + functions as described below. + + Callbacks + ========= + + A number of callback functions are available to receive data back from the + broker. To use a callback, define a function and then assign it to the + client: + + def on_connect(client, userdata, flags, rc, properties=None): + print("Connection returned " + str(rc)) + + client.on_connect = on_connect + + All of the callbacks as described below have a "client" and an "userdata" + argument. "client" is the Client instance that is calling the callback. + "userdata" is user data of any type and can be set when creating a new client + instance or with user_data_set(userdata). + + The callbacks: + + on_connect(client, userdata, flags, rc, properties=None): called when the broker responds to our connection + request. + flags is a dict that contains response flags from the broker: + flags['session present'] - this flag is useful for clients that are + using clean session set to 0 only. If a client with clean + session=0, that reconnects to a broker that it has previously + connected to, this flag indicates whether the broker still has the + session information for the client. If 1, the session still exists. + The value of rc determines success or not: + 0: Connection successful + 1: Connection refused - incorrect protocol version + 2: Connection refused - invalid client identifier + 3: Connection refused - server unavailable + 4: Connection refused - bad username or password + 5: Connection refused - not authorised + 6-255: Currently unused. + + on_disconnect(client, userdata, rc): called when the client disconnects from the broker. + The rc parameter indicates the disconnection state. If MQTT_ERR_SUCCESS + (0), the callback was called in response to a disconnect() call. If any + other value the disconnection was unexpected, such as might be caused by + a network error. + + on_disconnect(client, userdata, rc, properties): called when the MQTT V5 client disconnects from the broker. + When using MQTT V5, the broker can send a disconnect message to the client. The + message can contain a reason code and MQTT V5 properties. The properties parameter could be + None if they do not exist in the disconnect message. + + on_message(client, userdata, message): called when a message has been received on a + topic that the client subscribes to. The message variable is a + MQTTMessage that describes all of the message parameters. + + on_publish(client, userdata, mid): called when a message that was to be sent using the + publish() call has completed transmission to the broker. For messages + with QoS levels 1 and 2, this means that the appropriate handshakes have + completed. For QoS 0, this simply means that the message has left the + client. The mid variable matches the mid variable returned from the + corresponding publish() call, to allow outgoing messages to be tracked. + This callback is important because even if the publish() call returns + success, it does not always mean that the message has been sent. + + on_subscribe(client, userdata, mid, granted_qos, properties=None): called when the broker responds to a + subscribe request. The mid variable matches the mid variable returned + from the corresponding subscribe() call. The granted_qos variable is a + list of integers that give the QoS level the broker has granted for each + of the different subscription requests. + + on_unsubscribe(client, userdata, mid): called when the broker responds to an unsubscribe + request. The mid variable matches the mid variable returned from the + corresponding unsubscribe() call. + + on_log(client, userdata, level, buf): called when the client has log information. Define + to allow debugging. The level variable gives the severity of the message + and will be one of MQTT_LOG_INFO, MQTT_LOG_NOTICE, MQTT_LOG_WARNING, + MQTT_LOG_ERR, and MQTT_LOG_DEBUG. The message itself is in buf. + + on_socket_open(client, userdata, sock): Called when the socket has been opened. Use this + to register the socket with an external event loop for reading. + + on_socket_close(client, userdata, sock): Called when the socket is about to be closed. + Use this to unregister a socket from an external event loop for reading. + + on_socket_register_write(client, userdata, sock): Called when a write operation to the + socket failed because it would have blocked, e.g. output buffer full. Use this to + register the socket with an external event loop for writing. + + on_socket_unregister_write(client, userdata, sock): Called when a write operation to the + socket succeeded after it had previously failed. Use this to unregister the socket + from an external event loop for writing. + """ + + def __init__(self, client_id="", clean_session=None, userdata=None, + protocol=MQTTv311, transport="tcp"): + """client_id is the unique client id string used when connecting to the + broker. If client_id is zero length or None, then the behaviour is + defined by which protocol version is in use. If using MQTT v3.1.1, then + a zero length client id will be sent to the broker and the broker will + generate a random for the client. If using MQTT v3.1 then an id will be + randomly generated. In both cases, clean_session must be True. If this + is not the case a ValueError will be raised. + + clean_session is a boolean that determines the client type. If True, + the broker will remove all information about this client when it + disconnects. If False, the client is a persistent client and + subscription information and queued messages will be retained when the + client disconnects. + Note that a client will never discard its own outgoing messages on + disconnect. Calling connect() or reconnect() will cause the messages to + be resent. Use reinitialise() to reset a client to its original state. + The clean_session argument only applies to MQTT versions v3.1.1 and v3.1. + It is not accepted if the MQTT version is v5.0 - use the clean_start + argument on connect() instead. + + userdata is user defined data of any type that is passed as the "userdata" + parameter to callbacks. It may be updated at a later point with the + user_data_set() function. + + The protocol argument allows explicit setting of the MQTT version to + use for this client. Can be paho.mqtt.client.MQTTv311 (v3.1.1), + paho.mqtt.client.MQTTv31 (v3.1) or paho.mqtt.client.MQTTv5 (v5.0), + with the default being v3.1.1. + + Set transport to "websockets" to use WebSockets as the transport + mechanism. Set to "tcp" to use raw TCP, which is the default. + """ + + if protocol == MQTTv5: + if clean_session != None: + raise ValueError('Clean session is not used for MQTT 5.0') + else: + if clean_session == None: + clean_session = True + if not clean_session and (client_id == "" or client_id is None): + raise ValueError( + 'A client id must be provided if clean session is False.') + self._clean_session = clean_session + + if transport.lower() not in ('websockets', 'tcp'): + raise ValueError( + 'transport must be "websockets" or "tcp", not %s' % transport) + self._transport = transport.lower() + self._protocol = protocol + self._userdata = userdata + self._sock = None + self._sockpairR, self._sockpairW = (None, None,) + self._sockpairR, self._sockpairW = _socketpair_compat() + self._keepalive = 60 + self._message_retry = 20 + self._last_retry_check = 0 + self._client_mode = MQTT_CLIENT + # [MQTT-3.1.3-4] Client Id must be UTF-8 encoded string. + if client_id == "" or client_id is None: + if protocol == MQTTv31: + self._client_id = base62(uuid.uuid4().int, padding=22) + else: + self._client_id = b"" + else: + self._client_id = client_id + if isinstance(self._client_id, unicode): + self._client_id = self._client_id.encode('utf-8') + + self._username = None + self._password = None + self._in_packet = { + "command": 0, + "have_remaining": 0, + "remaining_count": [], + "remaining_mult": 1, + "remaining_length": 0, + "packet": b"", + "to_process": 0, + "pos": 0} + self._out_packet = collections.deque() + self._current_out_packet = None + self._last_msg_in = time_func() + self._last_msg_out = time_func() + self._reconnect_min_delay = 1 + self._reconnect_max_delay = 120 + self._reconnect_delay = None + self._ping_t = 0 + self._last_mid = 0 + self._state = mqtt_cs_new + self._out_messages = collections.OrderedDict() + self._in_messages = collections.OrderedDict() + self._max_inflight_messages = 20 + self._inflight_messages = 0 + self._max_queued_messages = 0 + self._connect_properties = None + self._will_properties = None + self._will = False + self._will_topic = b"" + self._will_payload = b"" + self._will_qos = 0 + self._will_retain = False + self._on_message_filtered = MQTTMatcher() + self._host = "" + self._port = 1883 + self._bind_address = "" + self._bind_port = 0 + self._proxy = {} + self._in_callback_mutex = threading.Lock() + self._callback_mutex = threading.RLock() + self._out_packet_mutex = threading.Lock() + self._current_out_packet_mutex = threading.RLock() + self._msgtime_mutex = threading.Lock() + self._out_message_mutex = threading.RLock() + self._in_message_mutex = threading.Lock() + self._reconnect_delay_mutex = threading.Lock() + self._mid_generate_mutex = threading.Lock() + self._thread = None + self._thread_terminate = False + self._ssl = False + self._ssl_context = None + # Only used when SSL context does not have check_hostname attribute + self._tls_insecure = False + self._logger = None + self._registered_write = False + # No default callbacks + self._on_log = None + self._on_connect = None + self._on_subscribe = None + self._on_message = None + self._on_publish = None + self._on_unsubscribe = None + self._on_disconnect = None + self._on_socket_open = None + self._on_socket_close = None + self._on_socket_register_write = None + self._on_socket_unregister_write = None + self._websocket_path = "/mqtt" + self._websocket_extra_headers = None + # for clean_start == MQTT_CLEAN_START_FIRST_ONLY + self._mqttv5_first_connect = True + + def __del__(self): + self._reset_sockets() + + def _sock_recv(self, bufsize): + try: + return self._sock.recv(bufsize) + except socket.error as err: + if self._ssl and err.errno == ssl.SSL_ERROR_WANT_READ: + raise WouldBlockError() + if self._ssl and err.errno == ssl.SSL_ERROR_WANT_WRITE: + self._call_socket_register_write() + raise WouldBlockError() + if err.errno == EAGAIN: + raise WouldBlockError() + raise + + def _sock_send(self, buf): + try: + return self._sock.send(buf) + except socket.error as err: + if self._ssl and err.errno == ssl.SSL_ERROR_WANT_READ: + raise WouldBlockError() + if self._ssl and err.errno == ssl.SSL_ERROR_WANT_WRITE: + self._call_socket_register_write() + raise WouldBlockError() + if err.errno == EAGAIN: + self._call_socket_register_write() + raise WouldBlockError() + raise + + def _sock_close(self): + """Close the connection to the server.""" + if not self._sock: + return + + try: + sock = self._sock + self._sock = None + self._call_socket_unregister_write(sock) + self._call_socket_close(sock) + finally: + # In case a callback fails, still close the socket to avoid leaking the file descriptor. + sock.close() + + def _reset_sockets(self): + self._sock_close() + + if self._sockpairR: + self._sockpairR.close() + self._sockpairR = None + if self._sockpairW: + self._sockpairW.close() + self._sockpairW = None + + def reinitialise(self, client_id="", clean_session=True, userdata=None): + self._reset_sockets() + + self.__init__(client_id, clean_session, userdata) + + def ws_set_options(self, path="/mqtt", headers=None): + """ Set the path and headers for a websocket connection + + path is a string starting with / which should be the endpoint of the + mqtt connection on the remote server + + headers can be either a dict or a callable object. If it is a dict then + the extra items in the dict are added to the websocket headers. If it is + a callable, then the default websocket headers are passed into this + function and the result is used as the new headers. + """ + self._websocket_path = path + + if headers is not None: + if isinstance(headers, dict) or callable(headers): + self._websocket_extra_headers = headers + else: + raise ValueError( + "'headers' option to ws_set_options has to be either a dictionary or callable") + + def tls_set_context(self, context=None): + """Configure network encryption and authentication context. Enables SSL/TLS support. + + context : an ssl.SSLContext object. By default this is given by + `ssl.create_default_context()`, if available. + + Must be called before connect() or connect_async().""" + if self._ssl_context is not None: + raise ValueError('SSL/TLS has already been configured.') + + # Assume that have SSL support, or at least that context input behaves like ssl.SSLContext + # in current versions of Python + + if context is None: + if hasattr(ssl, 'create_default_context'): + context = ssl.create_default_context() + else: + raise ValueError('SSL/TLS context must be specified') + + self._ssl = True + self._ssl_context = context + + # Ensure _tls_insecure is consistent with check_hostname attribute + if hasattr(context, 'check_hostname'): + self._tls_insecure = not context.check_hostname + + def tls_set(self, ca_certs=None, certfile=None, keyfile=None, cert_reqs=None, tls_version=None, ciphers=None): + """Configure network encryption and authentication options. Enables SSL/TLS support. + + ca_certs : a string path to the Certificate Authority certificate files + that are to be treated as trusted by this client. If this is the only + option given then the client will operate in a similar manner to a web + browser. That is to say it will require the broker to have a + certificate signed by the Certificate Authorities in ca_certs and will + communicate using TLS v1, but will not attempt any form of + authentication. This provides basic network encryption but may not be + sufficient depending on how the broker is configured. + By default, on Python 2.7.9+ or 3.4+, the default certification + authority of the system is used. On older Python version this parameter + is mandatory. + + certfile and keyfile are strings pointing to the PEM encoded client + certificate and private keys respectively. If these arguments are not + None then they will be used as client information for TLS based + authentication. Support for this feature is broker dependent. Note + that if either of these files in encrypted and needs a password to + decrypt it, Python will ask for the password at the command line. It is + not currently possible to define a callback to provide the password. + + cert_reqs allows the certificate requirements that the client imposes + on the broker to be changed. By default this is ssl.CERT_REQUIRED, + which means that the broker must provide a certificate. See the ssl + pydoc for more information on this parameter. + + tls_version allows the version of the SSL/TLS protocol used to be + specified. By default TLS v1 is used. Previous versions (all versions + beginning with SSL) are possible but not recommended due to possible + security problems. + + ciphers is a string specifying which encryption ciphers are allowable + for this connection, or None to use the defaults. See the ssl pydoc for + more information. + + Must be called before connect() or connect_async().""" + if ssl is None: + raise ValueError('This platform has no SSL/TLS.') + + if not hasattr(ssl, 'SSLContext'): + # Require Python version that has SSL context support in standard library + raise ValueError( + 'Python 2.7.9 and 3.2 are the minimum supported versions for TLS.') + + if ca_certs is None and not hasattr(ssl.SSLContext, 'load_default_certs'): + raise ValueError('ca_certs must not be None.') + + # Create SSLContext object + if tls_version is None: + tls_version = ssl.PROTOCOL_TLSv1 + # If the python version supports it, use highest TLS version automatically + if hasattr(ssl, "PROTOCOL_TLS"): + tls_version = ssl.PROTOCOL_TLS + context = ssl.SSLContext(tls_version) + + # Configure context + if certfile is not None: + context.load_cert_chain(certfile, keyfile) + + if cert_reqs == ssl.CERT_NONE and hasattr(context, 'check_hostname'): + context.check_hostname = False + + context.verify_mode = ssl.CERT_REQUIRED if cert_reqs is None else cert_reqs + + if ca_certs is not None: + context.load_verify_locations(ca_certs) + else: + context.load_default_certs() + + if ciphers is not None: + context.set_ciphers(ciphers) + + self.tls_set_context(context) + + if cert_reqs != ssl.CERT_NONE: + # Default to secure, sets context.check_hostname attribute + # if available + self.tls_insecure_set(False) + else: + # But with ssl.CERT_NONE, we can not check_hostname + self.tls_insecure_set(True) + + def tls_insecure_set(self, value): + """Configure verification of the server hostname in the server certificate. + + If value is set to true, it is impossible to guarantee that the host + you are connecting to is not impersonating your server. This can be + useful in initial server testing, but makes it possible for a malicious + third party to impersonate your server through DNS spoofing, for + example. + + Do not use this function in a real system. Setting value to true means + there is no point using encryption. + + Must be called before connect() and after either tls_set() or + tls_set_context().""" + + if self._ssl_context is None: + raise ValueError( + 'Must configure SSL context before using tls_insecure_set.') + + self._tls_insecure = value + + # Ensure check_hostname is consistent with _tls_insecure attribute + if hasattr(self._ssl_context, 'check_hostname'): + # Rely on SSLContext to check host name + # If verify_mode is CERT_NONE then the host name will never be checked + self._ssl_context.check_hostname = not value + + def proxy_set(self, **proxy_args): + """Configure proxying of MQTT connection. Enables support for SOCKS or + HTTP proxies. + + Proxying is done through the PySocks library. Brief descriptions of the + proxy_args parameters are below; see the PySocks docs for more info. + + (Required) + proxy_type: One of {socks.HTTP, socks.SOCKS4, or socks.SOCKS5} + proxy_addr: IP address or DNS name of proxy server + + (Optional) + proxy_rdns: boolean indicating whether proxy lookup should be performed + remotely (True, default) or locally (False) + proxy_username: username for SOCKS5 proxy, or userid for SOCKS4 proxy + proxy_password: password for SOCKS5 proxy + + Must be called before connect() or connect_async().""" + if socks is None: + raise ValueError("PySocks must be installed for proxy support.") + elif not self._proxy_is_valid(proxy_args): + raise ValueError("proxy_type and/or proxy_addr are invalid.") + else: + self._proxy = proxy_args + + def enable_logger(self, logger=None): + """ Enables a logger to send log messages to """ + if logger is None: + if self._logger is not None: + # Do not replace existing logger + return + logger = logging.getLogger(__name__) + self._logger = logger + + def disable_logger(self): + self._logger = None + + def connect(self, host, port=1883, keepalive=60, bind_address="", bind_port=0, + clean_start=MQTT_CLEAN_START_FIRST_ONLY, properties=None): + """Connect to a remote broker. + + host is the hostname or IP address of the remote broker. + port is the network port of the server host to connect to. Defaults to + 1883. Note that the default port for MQTT over SSL/TLS is 8883 so if you + are using tls_set() the port may need providing. + keepalive: Maximum period in seconds between communications with the + broker. If no other messages are being exchanged, this controls the + rate at which the client will send ping messages to the broker. + clean_start: (MQTT v5.0 only) True, False or MQTT_CLEAN_START_FIRST_ONLY. + Sets the MQTT v5.0 clean_start flag always, never or on the first successful connect only, + respectively. MQTT session data (such as outstanding messages and subscriptions) + is cleared on successful connect when the clean_start flag is set. + properties: (MQTT v5.0 only) the MQTT v5.0 properties to be sent in the + MQTT connect packet. + """ + + if self._protocol == MQTTv5: + self._mqttv5_first_connect == True + else: + if clean_start != MQTT_CLEAN_START_FIRST_ONLY: + raise ValueError("Clean start only applies to MQTT V5") + if properties != None: + raise ValueError("Properties only apply to MQTT V5") + + self.connect_async(host, port, keepalive, + bind_address, bind_port, clean_start, properties) + return self.reconnect() + + def connect_srv(self, domain=None, keepalive=60, bind_address="", + clean_start=MQTT_CLEAN_START_FIRST_ONLY, properties=None): + """Connect to a remote broker. + + domain is the DNS domain to search for SRV records; if None, + try to determine local domain name. + keepalive, bind_address, clean_start and properties are as for connect() + """ + + if HAVE_DNS is False: + raise ValueError( + 'No DNS resolver library found, try "pip install dnspython" or "pip3 install dnspython3".') + + if domain is None: + domain = socket.getfqdn() + domain = domain[domain.find('.') + 1:] + + try: + rr = '_mqtt._tcp.%s' % domain + if self._ssl: + # IANA specifies secure-mqtt (not mqtts) for port 8883 + rr = '_secure-mqtt._tcp.%s' % domain + answers = [] + for answer in dns.resolver.query(rr, dns.rdatatype.SRV): + addr = answer.target.to_text()[:-1] + answers.append( + (addr, answer.port, answer.priority, answer.weight)) + except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer, dns.resolver.NoNameservers): + raise ValueError("No answer/NXDOMAIN for SRV in %s" % (domain)) + + # FIXME: doesn't account for weight + for answer in answers: + host, port, prio, weight = answer + + try: + return self.connect(host, port, keepalive, bind_address, clean_start, properties) + except Exception: + pass + + raise ValueError("No SRV hosts responded") + + def connect_async(self, host, port=1883, keepalive=60, bind_address="", bind_port=0, + clean_start=MQTT_CLEAN_START_FIRST_ONLY, properties=None): + """Connect to a remote broker asynchronously. This is a non-blocking + connect call that can be used with loop_start() to provide very quick + start. + + host is the hostname or IP address of the remote broker. + port is the network port of the server host to connect to. Defaults to + 1883. Note that the default port for MQTT over SSL/TLS is 8883 so if you + are using tls_set() the port may need providing. + keepalive: Maximum period in seconds between communications with the + broker. If no other messages are being exchanged, this controls the + rate at which the client will send ping messages to the broker. + clean_start: (MQTT v5.0 only) True, False or MQTT_CLEAN_START_FIRST_ONLY. + Sets the MQTT v5.0 clean_start flag always, never or on the first successful connect only, + respectively. MQTT session data (such as outstanding messages and subscriptions) + is cleared on successful connect when the clean_start flag is set. + properties: (MQTT v5.0 only) the MQTT v5.0 properties to be sent in the + MQTT connect packet. Use the Properties class. + """ + if host is None or len(host) == 0: + raise ValueError('Invalid host.') + if port <= 0: + raise ValueError('Invalid port number.') + if keepalive < 0: + raise ValueError('Keepalive must be >=0.') + if bind_address != "" and bind_address is not None: + if sys.version_info < (2, 7) or (3, 0) < sys.version_info < (3, 2): + raise ValueError('bind_address requires Python 2.7 or 3.2.') + if bind_port < 0: + raise ValueError('Invalid bind port number.') + + self._host = host + self._port = port + self._keepalive = keepalive + self._bind_address = bind_address + self._bind_port = bind_port + self._clean_start = clean_start + self._connect_properties = properties + self._state = mqtt_cs_connect_async + + + def reconnect_delay_set(self, min_delay=1, max_delay=120): + """ Configure the exponential reconnect delay + + When connection is lost, wait initially min_delay seconds and + double this time every attempt. The wait is capped at max_delay. + Once the client is fully connected (e.g. not only TCP socket, but + received a success CONNACK), the wait timer is reset to min_delay. + """ + with self._reconnect_delay_mutex: + self._reconnect_min_delay = min_delay + self._reconnect_max_delay = max_delay + self._reconnect_delay = None + + def reconnect(self): + """Reconnect the client after a disconnect. Can only be called after + connect()/connect_async().""" + if len(self._host) == 0: + raise ValueError('Invalid host.') + if self._port <= 0: + raise ValueError('Invalid port number.') + + self._in_packet = { + "command": 0, + "have_remaining": 0, + "remaining_count": [], + "remaining_mult": 1, + "remaining_length": 0, + "packet": b"", + "to_process": 0, + "pos": 0} + + with self._out_packet_mutex: + self._out_packet = collections.deque() + + with self._current_out_packet_mutex: + self._current_out_packet = None + + with self._msgtime_mutex: + self._last_msg_in = time_func() + self._last_msg_out = time_func() + + self._ping_t = 0 + self._state = mqtt_cs_new + + self._sock_close() + + # Put messages in progress in a valid state. + self._messages_reconnect_reset() + + sock = self._create_socket_connection() + + if self._ssl: + # SSL is only supported when SSLContext is available (implies Python >= 2.7.9 or >= 3.2) + + verify_host = not self._tls_insecure + try: + # Try with server_hostname, even it's not supported in certain scenarios + sock = self._ssl_context.wrap_socket( + sock, + server_hostname=self._host, + do_handshake_on_connect=False, + ) + except ssl.CertificateError: + # CertificateError is derived from ValueError + raise + except ValueError: + # Python version requires SNI in order to handle server_hostname, but SNI is not available + sock = self._ssl_context.wrap_socket( + sock, + do_handshake_on_connect=False, + ) + else: + # If SSL context has already checked hostname, then don't need to do it again + if (hasattr(self._ssl_context, 'check_hostname') and + self._ssl_context.check_hostname): + verify_host = False + + sock.settimeout(self._keepalive) + sock.do_handshake() + + if verify_host: + ssl.match_hostname(sock.getpeercert(), self._host) + + if self._transport == "websockets": + sock.settimeout(self._keepalive) + sock = WebsocketWrapper(sock, self._host, self._port, self._ssl, + self._websocket_path, self._websocket_extra_headers) + + self._sock = sock + self._sock.setblocking(0) + self._registered_write = False + self._call_socket_open() + + return self._send_connect(self._keepalive) + + def loop(self, timeout=1.0, max_packets=1): + """Process network events. + + This function must be called regularly to ensure communication with the + broker is carried out. It calls select() on the network socket to wait + for network events. If incoming data is present it will then be + processed. Outgoing commands, from e.g. publish(), are normally sent + immediately that their function is called, but this is not always + possible. loop() will also attempt to send any remaining outgoing + messages, which also includes commands that are part of the flow for + messages with QoS>0. + + timeout: The time in seconds to wait for incoming/outgoing network + traffic before timing out and returning. + max_packets: Not currently used. + + Returns MQTT_ERR_SUCCESS on success. + Returns >0 on error. + + A ValueError will be raised if timeout < 0""" + if timeout < 0.0: + raise ValueError('Invalid timeout.') + + with self._current_out_packet_mutex: + with self._out_packet_mutex: + if self._current_out_packet is None and len(self._out_packet) > 0: + self._current_out_packet = self._out_packet.popleft() + + if self._current_out_packet: + wlist = [self._sock] + else: + wlist = [] + + # used to check if there are any bytes left in the (SSL) socket + pending_bytes = 0 + if hasattr(self._sock, 'pending'): + pending_bytes = self._sock.pending() + + # if bytes are pending do not wait in select + if pending_bytes > 0: + timeout = 0.0 + + # sockpairR is used to break out of select() before the timeout, on a + # call to publish() etc. + rlist = [self._sock, self._sockpairR] + try: + socklist = select.select(rlist, wlist, [], timeout) + except TypeError: + # Socket isn't correct type, in likelihood connection is lost + return MQTT_ERR_CONN_LOST + except ValueError: + # Can occur if we just reconnected but rlist/wlist contain a -1 for + # some reason. + return MQTT_ERR_CONN_LOST + except Exception: + # Note that KeyboardInterrupt, etc. can still terminate since they + # are not derived from Exception + return MQTT_ERR_UNKNOWN + + if self._sock in socklist[0] or pending_bytes > 0: + rc = self.loop_read(max_packets) + if rc or self._sock is None: + return rc + + if self._sockpairR in socklist[0]: + # Stimulate output write even though we didn't ask for it, because + # at that point the publish or other command wasn't present. + socklist[1].insert(0, self._sock) + # Clear sockpairR - only ever a single byte written. + try: + self._sockpairR.recv(1) + except socket.error as err: + if err.errno != EAGAIN: + raise + + if self._sock in socklist[1]: + rc = self.loop_write(max_packets) + if rc or self._sock is None: + return rc + + return self.loop_misc() + + def publish(self, topic, payload=None, qos=0, retain=False, properties=None): + """Publish a message on a topic. + + This causes a message to be sent to the broker and subsequently from + the broker to any clients subscribing to matching topics. + + topic: The topic that the message should be published on. + payload: The actual message to send. If not given, or set to None a + zero length message will be used. Passing an int or float will result + in the payload being converted to a string representing that number. If + you wish to send a true int/float, use struct.pack() to create the + payload you require. + qos: The quality of service level to use. + retain: If set to true, the message will be set as the "last known + good"/retained message for the topic. + properties: (MQTT v5.0 only) the MQTT v5.0 properties to be included. + Use the Properties class. + + Returns a MQTTMessageInfo class, which can be used to determine whether + the message has been delivered (using info.is_published()) or to block + waiting for the message to be delivered (info.wait_for_publish()). The + message ID and return code of the publish() call can be found at + info.mid and info.rc. + + For backwards compatibility, the MQTTMessageInfo class is iterable so + the old construct of (rc, mid) = client.publish(...) is still valid. + + rc is MQTT_ERR_SUCCESS to indicate success or MQTT_ERR_NO_CONN if the + client is not currently connected. mid is the message ID for the + publish request. The mid value can be used to track the publish request + by checking against the mid argument in the on_publish() callback if it + is defined. + + A ValueError will be raised if topic is None, has zero length or is + invalid (contains a wildcard), except if the MQTT version used is v5.0. + For v5.0, a zero length topic can be used when a Topic Alias has been set. + + A ValueError will be raised if qos is not one of 0, 1 or 2, or if + the length of the payload is greater than 268435455 bytes.""" + if self._protocol != MQTTv5: + if topic is None or len(topic) == 0: + raise ValueError('Invalid topic.') + + topic = topic.encode('utf-8') + + if self._topic_wildcard_len_check(topic) != MQTT_ERR_SUCCESS: + raise ValueError('Publish topic cannot contain wildcards.') + + if qos < 0 or qos > 2: + raise ValueError('Invalid QoS level.') + + if isinstance(payload, unicode): + local_payload = payload.encode('utf-8') + elif isinstance(payload, (bytes, bytearray)): + local_payload = payload + elif isinstance(payload, (int, float)): + local_payload = str(payload).encode('ascii') + elif payload is None: + local_payload = b'' + else: + raise TypeError( + 'payload must be a string, bytearray, int, float or None.') + + if len(local_payload) > 268435455: + raise ValueError('Payload too large.') + + local_mid = self._mid_generate() + + if qos == 0: + info = MQTTMessageInfo(local_mid) + rc = self._send_publish( + local_mid, topic, local_payload, qos, retain, False, info, properties) + info.rc = rc + return info + else: + message = MQTTMessage(local_mid, topic) + message.timestamp = time_func() + message.payload = local_payload + message.qos = qos + message.retain = retain + message.dup = False + message.properties = properties + + with self._out_message_mutex: + if self._max_queued_messages > 0 and len(self._out_messages) >= self._max_queued_messages: + message.info.rc = MQTT_ERR_QUEUE_SIZE + return message.info + + if local_mid in self._out_messages: + message.info.rc = MQTT_ERR_QUEUE_SIZE + return message.info + + self._out_messages[message.mid] = message + if self._max_inflight_messages == 0 or self._inflight_messages < self._max_inflight_messages: + self._inflight_messages += 1 + if qos == 1: + message.state = mqtt_ms_wait_for_puback + elif qos == 2: + message.state = mqtt_ms_wait_for_pubrec + + rc = self._send_publish(message.mid, topic, message.payload, message.qos, message.retain, + message.dup, message.info, message.properties) + + # remove from inflight messages so it will be send after a connection is made + if rc is MQTT_ERR_NO_CONN: + self._inflight_messages -= 1 + message.state = mqtt_ms_publish + + message.info.rc = rc + return message.info + else: + message.state = mqtt_ms_queued + message.info.rc = MQTT_ERR_SUCCESS + return message.info + + def username_pw_set(self, username, password=None): + """Set a username and optionally a password for broker authentication. + + Must be called before connect() to have any effect. + Requires a broker that supports MQTT v3.1. + + username: The username to authenticate with. Need have no relationship to the client id. Must be unicode + [MQTT-3.1.3-11]. + Set to None to reset client back to not using username/password for broker authentication. + password: The password to authenticate with. Optional, set to None if not required. If it is unicode, then it + will be encoded as UTF-8. + """ + + # [MQTT-3.1.3-11] User name must be UTF-8 encoded string + self._username = None if username is None else username.encode('utf-8') + self._password = password + if isinstance(self._password, unicode): + self._password = self._password.encode('utf-8') + + def enable_bridge_mode(self): + """Sets the client in a bridge mode instead of client mode. + + Must be called before connect() to have any effect. + Requires brokers that support bridge mode. + + Under bridge mode, the broker will identify the client as a bridge and + not send it's own messages back to it. Hence a subsciption of # is + possible without message loops. This feature also correctly propagates + the retain flag on the messages. + + Currently Mosquitto and RSMB support this feature. This feature can + be used to create a bridge between multiple broker. + """ + self._client_mode = MQTT_BRIDGE + + def is_connected(self): + """Returns the current status of the connection + + True if connection exists + False if connection is closed + """ + return self._state == mqtt_cs_connected + + def disconnect(self, reasoncode=None, properties=None): + """Disconnect a connected client from the broker. + reasoncode: (MQTT v5.0 only) a ReasonCodes instance setting the MQTT v5.0 + reasoncode to be sent with the disconnect. It is optional, the receiver + then assuming that 0 (success) is the value. + properties: (MQTT v5.0 only) a Properties instance setting the MQTT v5.0 properties + to be included. Optional - if not set, no properties are sent. + """ + self._state = mqtt_cs_disconnecting + + if self._sock is None: + return MQTT_ERR_NO_CONN + + return self._send_disconnect(reasoncode, properties) + + def subscribe(self, topic, qos=0, options=None, properties=None): + """Subscribe the client to one or more topics. + + This function may be called in three different ways (and a further three for MQTT v5.0): + + Simple string and integer + ------------------------- + e.g. subscribe("my/topic", 2) + + topic: A string specifying the subscription topic to subscribe to. + qos: The desired quality of service level for the subscription. + Defaults to 0. + options and properties: Not used. + + Simple string and subscribe options (MQTT v5.0 only) + ---------------------------------------------------- + e.g. subscribe("my/topic", options=SubscribeOptions(qos=2)) + + topic: A string specifying the subscription topic to subscribe to. + qos: Not used. + options: The MQTT v5.0 subscribe options. + properties: a Properties instance setting the MQTT v5.0 properties + to be included. Optional - if not set, no properties are sent. + + String and integer tuple + ------------------------ + e.g. subscribe(("my/topic", 1)) + + topic: A tuple of (topic, qos). Both topic and qos must be present in + the tuple. + qos and options: Not used. + properties: Only used for MQTT v5.0. A Properties instance setting the + MQTT v5.0 properties. Optional - if not set, no properties are sent. + + String and subscribe options tuple (MQTT v5.0 only) + --------------------------------------------------- + e.g. subscribe(("my/topic", SubscribeOptions(qos=1))) + + topic: A tuple of (topic, SubscribeOptions). Both topic and subscribe + options must be present in the tuple. + qos and options: Not used. + properties: a Properties instance setting the MQTT v5.0 properties + to be included. Optional - if not set, no properties are sent. + + List of string and integer tuples + --------------------------------- + e.g. subscribe([("my/topic", 0), ("another/topic", 2)]) + + This allows multiple topic subscriptions in a single SUBSCRIPTION + command, which is more efficient than using multiple calls to + subscribe(). + + topic: A list of tuple of format (topic, qos). Both topic and qos must + be present in all of the tuples. + qos, options and properties: Not used. + + List of string and subscribe option tuples (MQTT v5.0 only) + ----------------------------------------------------------- + e.g. subscribe([("my/topic", SubscribeOptions(qos=0), ("another/topic", SubscribeOptions(qos=2)]) + + This allows multiple topic subscriptions in a single SUBSCRIPTION + command, which is more efficient than using multiple calls to + subscribe(). + + topic: A list of tuple of format (topic, SubscribeOptions). Both topic and subscribe + options must be present in all of the tuples. + qos and options: Not used. + properties: a Properties instance setting the MQTT v5.0 properties + to be included. Optional - if not set, no properties are sent. + + The function returns a tuple (result, mid), where result is + MQTT_ERR_SUCCESS to indicate success or (MQTT_ERR_NO_CONN, None) if the + client is not currently connected. mid is the message ID for the + subscribe request. The mid value can be used to track the subscribe + request by checking against the mid argument in the on_subscribe() + callback if it is defined. + + Raises a ValueError if qos is not 0, 1 or 2, or if topic is None or has + zero string length, or if topic is not a string, tuple or list. + """ + topic_qos_list = None + + if isinstance(topic, tuple): + if self._protocol == MQTTv5: + topic, options = topic + if not isinstance(options, SubscribeOptions): + raise ValueError( + 'Subscribe options must be instance of SubscribeOptions class.') + else: + topic, qos = topic + + if isinstance(topic, basestring): + if qos < 0 or qos > 2: + raise ValueError('Invalid QoS level.') + if self._protocol == MQTTv5: + if options == None: + # if no options are provided, use the QoS passed instead + options = SubscribeOptions(qos=qos) + elif qos != 0: + raise ValueError( + 'Subscribe options and qos parameters cannot be combined.') + if not isinstance(options, SubscribeOptions): + raise ValueError( + 'Subscribe options must be instance of SubscribeOptions class.') + topic_qos_list = [(topic.encode('utf-8'), options)] + else: + if topic is None or len(topic) == 0: + raise ValueError('Invalid topic.') + topic_qos_list = [(topic.encode('utf-8'), qos)] + elif isinstance(topic, list): + topic_qos_list = [] + if self._protocol == MQTTv5: + for t, o in topic: + if not isinstance(o, SubscribeOptions): + # then the second value should be QoS + if o < 0 or o > 2: + raise ValueError('Invalid QoS level.') + o = SubscribeOptions(qos=o) + topic_qos_list.append((t.encode('utf-8'), o)) + else: + for t, q in topic: + if q < 0 or q > 2: + raise ValueError('Invalid QoS level.') + if t is None or len(t) == 0 or not isinstance(t, basestring): + raise ValueError('Invalid topic.') + topic_qos_list.append((t.encode('utf-8'), q)) + + if topic_qos_list is None: + raise ValueError("No topic specified, or incorrect topic type.") + + if any(self._filter_wildcard_len_check(topic) != MQTT_ERR_SUCCESS for topic, _ in topic_qos_list): + raise ValueError('Invalid subscription filter.') + + if self._sock is None: + return (MQTT_ERR_NO_CONN, None) + + return self._send_subscribe(False, topic_qos_list, properties) + + def unsubscribe(self, topic, properties=None): + """Unsubscribe the client from one or more topics. + + topic: A single string, or list of strings that are the subscription + topics to unsubscribe from. + properties: (MQTT v5.0 only) a Properties instance setting the MQTT v5.0 properties + to be included. Optional - if not set, no properties are sent. + + Returns a tuple (result, mid), where result is MQTT_ERR_SUCCESS + to indicate success or (MQTT_ERR_NO_CONN, None) if the client is not + currently connected. + mid is the message ID for the unsubscribe request. The mid value can be + used to track the unsubscribe request by checking against the mid + argument in the on_unsubscribe() callback if it is defined. + + Raises a ValueError if topic is None or has zero string length, or is + not a string or list. + """ + topic_list = None + if topic is None: + raise ValueError('Invalid topic.') + if isinstance(topic, basestring): + if len(topic) == 0: + raise ValueError('Invalid topic.') + topic_list = [topic.encode('utf-8')] + elif isinstance(topic, list): + topic_list = [] + for t in topic: + if len(t) == 0 or not isinstance(t, basestring): + raise ValueError('Invalid topic.') + topic_list.append(t.encode('utf-8')) + + if topic_list is None: + raise ValueError("No topic specified, or incorrect topic type.") + + if self._sock is None: + return (MQTT_ERR_NO_CONN, None) + + return self._send_unsubscribe(False, topic_list, properties) + + def loop_read(self, max_packets=1): + """Process read network events. Use in place of calling loop() if you + wish to handle your client reads as part of your own application. + + Use socket() to obtain the client socket to call select() or equivalent + on. + + Do not use if you are using the threaded interface loop_start().""" + if self._sock is None: + return MQTT_ERR_NO_CONN + + max_packets = len(self._out_messages) + len(self._in_messages) + if max_packets < 1: + max_packets = 1 + + for _ in range(0, max_packets): + if self._sock is None: + return MQTT_ERR_NO_CONN + rc = self._packet_read() + if rc > 0: + return self._loop_rc_handle(rc) + elif rc == MQTT_ERR_AGAIN: + return MQTT_ERR_SUCCESS + return MQTT_ERR_SUCCESS + + def loop_write(self, max_packets=1): + """Process write network events. Use in place of calling loop() if you + wish to handle your client writes as part of your own application. + + Use socket() to obtain the client socket to call select() or equivalent + on. + + Use want_write() to determine if there is data waiting to be written. + + Do not use if you are using the threaded interface loop_start().""" + if self._sock is None: + return MQTT_ERR_NO_CONN + + max_packets = len(self._out_packet) + 1 + if max_packets < 1: + max_packets = 1 + + try: + for _ in range(0, max_packets): + rc = self._packet_write() + if rc > 0: + return self._loop_rc_handle(rc) + elif rc == MQTT_ERR_AGAIN: + return MQTT_ERR_SUCCESS + return MQTT_ERR_SUCCESS + finally: + if self.want_write(): + self._call_socket_register_write() + else: + self._call_socket_unregister_write() + + def want_write(self): + """Call to determine if there is network data waiting to be written. + Useful if you are calling select() yourself rather than using loop(). + """ + if self._current_out_packet or len(self._out_packet) > 0: + return True + else: + return False + + def loop_misc(self): + """Process miscellaneous network events. Use in place of calling loop() if you + wish to call select() or equivalent on. + + Do not use if you are using the threaded interface loop_start().""" + if self._sock is None: + return MQTT_ERR_NO_CONN + + now = time_func() + self._check_keepalive() + if self._last_retry_check + 1 < now: + # Only check once a second at most + self._message_retry_check() + self._last_retry_check = now + + if self._ping_t > 0 and now - self._ping_t >= self._keepalive: + # client->ping_t != 0 means we are waiting for a pingresp. + # This hasn't happened in the keepalive time so we should disconnect. + self._sock_close() + + if self._state == mqtt_cs_disconnecting: + rc = MQTT_ERR_SUCCESS + else: + rc = 1 + + with self._callback_mutex: + if self.on_disconnect: + with self._in_callback_mutex: + try: + self.on_disconnect(self, self._userdata, rc) + except Exception as err: + self._easy_log( + MQTT_LOG_ERR, 'Caught exception in on_disconnect: %s', err) + + return MQTT_ERR_CONN_LOST + + return MQTT_ERR_SUCCESS + + def max_inflight_messages_set(self, inflight): + """Set the maximum number of messages with QoS>0 that can be part way + through their network flow at once. Defaults to 20.""" + if inflight < 0: + raise ValueError('Invalid inflight.') + self._max_inflight_messages = inflight + + def max_queued_messages_set(self, queue_size): + """Set the maximum number of messages in the outgoing message queue. + 0 means unlimited.""" + if queue_size < 0: + raise ValueError('Invalid queue size.') + if not isinstance(queue_size, int): + raise ValueError('Invalid type of queue size.') + self._max_queued_messages = queue_size + return self + + def message_retry_set(self, retry): + """Set the timeout in seconds before a message with QoS>0 is retried. + 20 seconds by default.""" + if retry < 0: + raise ValueError('Invalid retry.') + + self._message_retry = retry + + def user_data_set(self, userdata): + """Set the user data variable passed to callbacks. May be any data type.""" + self._userdata = userdata + + def will_set(self, topic, payload=None, qos=0, retain=False, properties=None): + """Set a Will to be sent by the broker in case the client disconnects unexpectedly. + + This must be called before connect() to have any effect. + + topic: The topic that the will message should be published on. + payload: The message to send as a will. If not given, or set to None a + zero length message will be used as the will. Passing an int or float + will result in the payload being converted to a string representing + that number. If you wish to send a true int/float, use struct.pack() to + create the payload you require. + qos: The quality of service level to use for the will. + retain: If set to true, the will message will be set as the "last known + good"/retained message for the topic. + properties: (MQTT v5.0 only) a Properties instance setting the MQTT v5.0 properties + to be included with the will message. Optional - if not set, no properties are sent. + + Raises a ValueError if qos is not 0, 1 or 2, or if topic is None or has + zero string length. + """ + if topic is None or len(topic) == 0: + raise ValueError('Invalid topic.') + + if qos < 0 or qos > 2: + raise ValueError('Invalid QoS level.') + + if properties != None and not isinstance(properties, Properties): + raise ValueError( + "The properties argument must be an instance of the Properties class.") + + if isinstance(payload, unicode): + self._will_payload = payload.encode('utf-8') + elif isinstance(payload, (bytes, bytearray)): + self._will_payload = payload + elif isinstance(payload, (int, float)): + self._will_payload = str(payload).encode('ascii') + elif payload is None: + self._will_payload = b"" + else: + raise TypeError( + 'payload must be a string, bytearray, int, float or None.') + + self._will = True + self._will_topic = topic.encode('utf-8') + self._will_qos = qos + self._will_retain = retain + self._will_properties = properties + + def will_clear(self): + """ Removes a will that was previously configured with will_set(). + + Must be called before connect() to have any effect.""" + self._will = False + self._will_topic = b"" + self._will_payload = b"" + self._will_qos = 0 + self._will_retain = False + + def socket(self): + """Return the socket or ssl object for this client.""" + return self._sock + + def loop_forever(self, timeout=1.0, max_packets=1, retry_first_connection=False): + """This function call loop() for you in an infinite blocking loop. It + is useful for the case where you only want to run the MQTT client loop + in your program. + + loop_forever() will handle reconnecting for you. If you call + disconnect() in a callback it will return. + + + timeout: The time in seconds to wait for incoming/outgoing network + traffic before timing out and returning. + max_packets: Not currently used. + retry_first_connection: Should the first connection attempt be retried on failure. + + Raises socket.error on first connection failures unless retry_first_connection=True + """ + + run = True + + while run: + if self._thread_terminate is True: + break + + if self._state == mqtt_cs_connect_async: + try: + self.reconnect() + except (socket.error, OSError, WebsocketConnectionError): + if not retry_first_connection: + raise + self._easy_log( + MQTT_LOG_DEBUG, "Connection failed, retrying") + self._reconnect_wait() + else: + break + + while run: + rc = MQTT_ERR_SUCCESS + while rc == MQTT_ERR_SUCCESS: + rc = self.loop(timeout, max_packets) + # We don't need to worry about locking here, because we've + # either called loop_forever() when in single threaded mode, or + # in multi threaded mode when loop_stop() has been called and + # so no other threads can access _current_out_packet, + # _out_packet or _messages. + if (self._thread_terminate is True + and self._current_out_packet is None + and len(self._out_packet) == 0 + and len(self._out_messages) == 0): + rc = 1 + run = False + + def should_exit(): + return self._state == mqtt_cs_disconnecting or run is False or self._thread_terminate is True + + if should_exit(): + run = False + else: + self._reconnect_wait() + + if should_exit(): + run = False + else: + try: + self.reconnect() + except (socket.error, OSError, WebsocketConnectionError): + self._easy_log( + MQTT_LOG_DEBUG, "Connection failed, retrying") + + return rc + + def loop_start(self): + """This is part of the threaded client interface. Call this once to + start a new thread to process network traffic. This provides an + alternative to repeatedly calling loop() yourself. + """ + if self._thread is not None: + return MQTT_ERR_INVAL + + self._thread_terminate = False + self._thread = threading.Thread(target=self._thread_main) + self._thread.daemon = True + self._thread.start() + + def loop_stop(self, force=False): + """This is part of the threaded client interface. Call this once to + stop the network thread previously created with loop_start(). This call + will block until the network thread finishes. + + The force parameter is currently ignored. + """ + if self._thread is None: + return MQTT_ERR_INVAL + + self._thread_terminate = True + if threading.current_thread() != self._thread: + self._thread.join() + self._thread = None + + @property + def on_log(self): + """If implemented, called when the client has log information. + Defined to allow debugging.""" + return self._on_log + + @on_log.setter + def on_log(self, func): + """ Define the logging callback implementation. + + Expected signature is: + log_callback(client, userdata, level, buf) + + client: the client instance for this callback + userdata: the private user data as set in Client() or userdata_set() + level: gives the severity of the message and will be one of + MQTT_LOG_INFO, MQTT_LOG_NOTICE, MQTT_LOG_WARNING, + MQTT_LOG_ERR, and MQTT_LOG_DEBUG. + buf: the message itself + """ + self._on_log = func + + @property + def on_connect(self): + """If implemented, called when the broker responds to our connection + request.""" + return self._on_connect + + @on_connect.setter + def on_connect(self, func): + """ Define the connect callback implementation. + + Expected signature for MQTT v3.1 and v3.1.1 is: + connect_callback(client, userdata, flags, rc, properties=None) + + and for MQTT v5.0: + connect_callback(client, userdata, flags, reasonCode, properties) + + client: the client instance for this callback + userdata: the private user data as set in Client() or userdata_set() + flags: response flags sent by the broker + rc: the connection result + reasonCode: the MQTT v5.0 reason code: an instance of the ReasonCode class. + ReasonCode may be compared to interger. + properties: the MQTT v5.0 properties returned from the broker. An instance + of the Properties class. + For MQTT v3.1 and v3.1.1 properties is not provided but for compatibility + with MQTT v5.0, we recommand adding properties=None. + + flags is a dict that contains response flags from the broker: + flags['session present'] - this flag is useful for clients that are + using clean session set to 0 only. If a client with clean + session=0, that reconnects to a broker that it has previously + connected to, this flag indicates whether the broker still has the + session information for the client. If 1, the session still exists. + + The value of rc indicates success or not: + 0: Connection successful + 1: Connection refused - incorrect protocol version + 2: Connection refused - invalid client identifier + 3: Connection refused - server unavailable + 4: Connection refused - bad username or password + 5: Connection refused - not authorised + 6-255: Currently unused. + """ + with self._callback_mutex: + self._on_connect = func + + @property + def on_subscribe(self): + """If implemented, called when the broker responds to a subscribe + request.""" + return self._on_subscribe + + @on_subscribe.setter + def on_subscribe(self, func): + """ Define the suscribe callback implementation. + + Expected signature for MQTT v3.1.1 and v3.1 is: + subscribe_callback(client, userdata, mid, granted_qos, properties=None) + + and for MQTT v5.0: + subscribe_callback(client, userdata, mid, reasonCodes, properties) + + client: the client instance for this callback + userdata: the private user data as set in Client() or userdata_set() + mid: matches the mid variable returned from the corresponding + subscribe() call. + granted_qos: list of integers that give the QoS level the broker has + granted for each of the different subscription requests. + reasonCodes: the MQTT v5.0 reason codes received from the broker for each + subscription. A list of ReasonCodes instances. + properties: the MQTT v5.0 properties received from the broker. A + list of Properties class instances. + """ + with self._callback_mutex: + self._on_subscribe = func + + @property + def on_message(self): + """If implemented, called when a message has been received on a topic + that the client subscribes to. + + This callback will be called for every message received. Use + message_callback_add() to define multiple callbacks that will be called + for specific topic filters.""" + return self._on_message + + @on_message.setter + def on_message(self, func): + """ Define the message received callback implementation. + + Expected signature is: + on_message_callback(client, userdata, message) + + client: the client instance for this callback + userdata: the private user data as set in Client() or userdata_set() + message: an instance of MQTTMessage. + This is a class with members topic, payload, qos, retain. + """ + with self._callback_mutex: + self._on_message = func + + @property + def on_publish(self): + """If implemented, called when a message that was to be sent using the + publish() call has completed transmission to the broker. + + For messages with QoS levels 1 and 2, this means that the appropriate + handshakes have completed. For QoS 0, this simply means that the message + has left the client. + This callback is important because even if the publish() call returns + success, it does not always mean that the message has been sent.""" + return self._on_publish + + @on_publish.setter + def on_publish(self, func): + """ Define the published message callback implementation. + + Expected signature is: + on_publish_callback(client, userdata, mid) + + client: the client instance for this callback + userdata: the private user data as set in Client() or userdata_set() + mid: matches the mid variable returned from the corresponding + publish() call, to allow outgoing messages to be tracked. + """ + with self._callback_mutex: + self._on_publish = func + + @property + def on_unsubscribe(self): + """If implemented, called when the broker responds to an unsubscribe + request.""" + return self._on_unsubscribe + + @on_unsubscribe.setter + def on_unsubscribe(self, func): + """ Define the unsubscribe callback implementation. + + Expected signature for MQTT v3.1.1 and v3.1 is: + unsubscribe_callback(client, userdata, mid) + + and for MQTT v5.0: + unsubscribe_callback(client, userdata, mid, properties, reasonCodes) + + client: the client instance for this callback + userdata: the private user data as set in Client() or userdata_set() + mid: matches the mid variable returned from the corresponding + unsubscribe() call. + properties: the MQTT v5.0 properties received from the broker. A + list of Properties class instances. + reasonCodes: the MQTT v5.0 reason codes received from the broker for each + unsubscribe topic. A list of ReasonCodes instances + """ + with self._callback_mutex: + self._on_unsubscribe = func + + @property + def on_disconnect(self): + """If implemented, called when the client disconnects from the broker. + """ + return self._on_disconnect + + @on_disconnect.setter + def on_disconnect(self, func): + """ Define the disconnect callback implementation. + + Expected signature for MQTT v3.1.1 and v3.1 is: + disconnect_callback(client, userdata, rc) + + and for MQTT v5.0: + disconnect_callback(client, userdata, reasonCode, properties) + + client: the client instance for this callback + userdata: the private user data as set in Client() or userdata_set() + rc: the disconnection result + The rc parameter indicates the disconnection state. If + MQTT_ERR_SUCCESS (0), the callback was called in response to + a disconnect() call. If any other value the disconnection + was unexpected, such as might be caused by a network error. + """ + with self._callback_mutex: + self._on_disconnect = func + + @property + def on_socket_open(self): + """If implemented, called just after the socket was opend.""" + return self._on_socket_open + + @on_socket_open.setter + def on_socket_open(self, func): + """Define the socket_open callback implementation. + + This should be used to register the socket to an external event loop for reading. + + Expected signature is: + socket_open_callback(client, userdata, socket) + + client: the client instance for this callback + userdata: the private user data as set in Client() or userdata_set() + sock: the socket which was just opened. + """ + with self._callback_mutex: + self._on_socket_open = func + + def _call_socket_open(self): + """Call the socket_open callback with the just-opened socket""" + with self._callback_mutex: + if self.on_socket_open: + with self._in_callback_mutex: + try: + self.on_socket_open(self, self._userdata, self._sock) + except Exception as err: + self._easy_log( + MQTT_LOG_ERR, 'Caught exception in on_socket_open: %s', err) + + @property + def on_socket_close(self): + """If implemented, called just before the socket is closed.""" + return self._on_socket_close + + @on_socket_close.setter + def on_socket_close(self, func): + """Define the socket_close callback implementation. + + This should be used to unregister the socket from an external event loop for reading. + + Expected signature is: + socket_close_callback(client, userdata, socket) + + client: the client instance for this callback + userdata: the private user data as set in Client() or userdata_set() + sock: the socket which is about to be closed. + """ + with self._callback_mutex: + self._on_socket_close = func + + def _call_socket_close(self, sock): + """Call the socket_close callback with the about-to-be-closed socket""" + with self._callback_mutex: + if self.on_socket_close: + with self._in_callback_mutex: + try: + self.on_socket_close(self, self._userdata, sock) + except Exception as err: + self._easy_log( + MQTT_LOG_ERR, 'Caught exception in on_socket_close: %s', err) + + @property + def on_socket_register_write(self): + """If implemented, called when the socket needs writing but can't.""" + return self._on_socket_register_write + + @on_socket_register_write.setter + def on_socket_register_write(self, func): + """Define the socket_register_write callback implementation. + + This should be used to register the socket with an external event loop for writing. + + Expected signature is: + socket_register_write_callback(client, userdata, socket) + + client: the client instance for this callback + userdata: the private user data as set in Client() or userdata_set() + sock: the socket which should be registered for writing + """ + with self._callback_mutex: + self._on_socket_register_write = func + + def _call_socket_register_write(self): + """Call the socket_register_write callback with the unwritable socket""" + if not self._sock or self._registered_write: + return + self._registered_write = True + with self._callback_mutex: + if self.on_socket_register_write: + try: + self.on_socket_register_write( + self, self._userdata, self._sock) + except Exception as err: + self._easy_log( + MQTT_LOG_ERR, 'Caught exception in on_socket_register_write: %s', err) + + @property + def on_socket_unregister_write(self): + """If implemented, called when the socket doesn't need writing anymore.""" + return self._on_socket_unregister_write + + @on_socket_unregister_write.setter + def on_socket_unregister_write(self, func): + """Define the socket_unregister_write callback implementation. + + This should be used to unregister the socket from an external event loop for writing. + + Expected signature is: + socket_unregister_write_callback(client, userdata, socket) + + client: the client instance for this callback + userdata: the private user data as set in Client() or userdata_set() + sock: the socket which should be unregistered for writing + """ + with self._callback_mutex: + self._on_socket_unregister_write = func + + def _call_socket_unregister_write(self, sock=None): + """Call the socket_unregister_write callback with the writable socket""" + sock = sock or self._sock + if not sock or not self._registered_write: + return + self._registered_write = False + + with self._callback_mutex: + if self.on_socket_unregister_write: + try: + self.on_socket_unregister_write(self, self._userdata, sock) + except Exception as err: + self._easy_log( + MQTT_LOG_ERR, 'Caught exception in on_socket_unregister_write: %s', err) + + def message_callback_add(self, sub, callback): + """Register a message callback for a specific topic. + Messages that match 'sub' will be passed to 'callback'. Any + non-matching messages will be passed to the default on_message + callback. + + Call multiple times with different 'sub' to define multiple topic + specific callbacks. + + Topic specific callbacks may be removed with + message_callback_remove().""" + if callback is None or sub is None: + raise ValueError("sub and callback must both be defined.") + + with self._callback_mutex: + self._on_message_filtered[sub] = callback + + def message_callback_remove(self, sub): + """Remove a message callback previously registered with + message_callback_add().""" + if sub is None: + raise ValueError("sub must defined.") + + with self._callback_mutex: + try: + del self._on_message_filtered[sub] + except KeyError: # no such subscription + pass + + # ============================================================ + # Private functions + # ============================================================ + + def _loop_rc_handle(self, rc, properties=None): + if rc: + self._sock_close() + + if self._state == mqtt_cs_disconnecting: + rc = MQTT_ERR_SUCCESS + + with self._callback_mutex: + if self.on_disconnect: + with self._in_callback_mutex: + try: + if properties: + self.on_disconnect( + self, self._userdata, rc, properties) + else: + self.on_disconnect(self, self._userdata, rc) + except Exception as err: + self._easy_log( + MQTT_LOG_ERR, 'Caught exception in on_disconnect: %s', err) + return rc + + def _packet_read(self): + # This gets called if pselect() indicates that there is network data + # available - ie. at least one byte. What we do depends on what data we + # already have. + # If we've not got a command, attempt to read one and save it. This should + # always work because it's only a single byte. + # Then try to read the remaining length. This may fail because it is may + # be more than one byte - will need to save data pending next read if it + # does fail. + # Then try to read the remaining payload, where 'payload' here means the + # combined variable header and actual payload. This is the most likely to + # fail due to longer length, so save current data and current position. + # After all data is read, send to _mqtt_handle_packet() to deal with. + # Finally, free the memory and reset everything to starting conditions. + if self._in_packet['command'] == 0: + try: + command = self._sock_recv(1) + except WouldBlockError: + return MQTT_ERR_AGAIN + except socket.error as err: + self._easy_log( + MQTT_LOG_ERR, 'failed to receive on socket: %s', err) + return 1 + else: + if len(command) == 0: + return 1 + command, = struct.unpack("!B", command) + self._in_packet['command'] = command + + if self._in_packet['have_remaining'] == 0: + # Read remaining + # Algorithm for decoding taken from pseudo code at + # http://publib.boulder.ibm.com/infocenter/wmbhelp/v6r0m0/topic/com.ibm.etools.mft.doc/ac10870_.htm + while True: + try: + byte = self._sock_recv(1) + except WouldBlockError: + return MQTT_ERR_AGAIN + except socket.error as err: + self._easy_log( + MQTT_LOG_ERR, 'failed to receive on socket: %s', err) + return 1 + else: + if len(byte) == 0: + return 1 + byte, = struct.unpack("!B", byte) + self._in_packet['remaining_count'].append(byte) + # Max 4 bytes length for remaining length as defined by protocol. + # Anything more likely means a broken/malicious client. + if len(self._in_packet['remaining_count']) > 4: + return MQTT_ERR_PROTOCOL + + self._in_packet['remaining_length'] += ( + byte & 127) * self._in_packet['remaining_mult'] + self._in_packet['remaining_mult'] = self._in_packet['remaining_mult'] * 128 + + if (byte & 128) == 0: + break + + self._in_packet['have_remaining'] = 1 + self._in_packet['to_process'] = self._in_packet['remaining_length'] + + while self._in_packet['to_process'] > 0: + try: + data = self._sock_recv(self._in_packet['to_process']) + except WouldBlockError: + return MQTT_ERR_AGAIN + except socket.error as err: + self._easy_log( + MQTT_LOG_ERR, 'failed to receive on socket: %s', err) + return 1 + else: + if len(data) == 0: + return 1 + self._in_packet['to_process'] -= len(data) + self._in_packet['packet'] += data + + # All data for this packet is read. + self._in_packet['pos'] = 0 + rc = self._packet_handle() + + # Free data and reset values + self._in_packet = { + 'command': 0, + 'have_remaining': 0, + 'remaining_count': [], + 'remaining_mult': 1, + 'remaining_length': 0, + 'packet': b"", + 'to_process': 0, + 'pos': 0} + + with self._msgtime_mutex: + self._last_msg_in = time_func() + return rc + + def _packet_write(self): + self._current_out_packet_mutex.acquire() + + while self._current_out_packet: + packet = self._current_out_packet + + try: + write_length = self._sock_send( + packet['packet'][packet['pos']:]) + except (AttributeError, ValueError): + self._current_out_packet_mutex.release() + return MQTT_ERR_SUCCESS + except WouldBlockError: + self._current_out_packet_mutex.release() + return MQTT_ERR_AGAIN + except socket.error as err: + self._current_out_packet_mutex.release() + self._easy_log( + MQTT_LOG_ERR, 'failed to receive on socket: %s', err) + return 1 + + if write_length > 0: + packet['to_process'] -= write_length + packet['pos'] += write_length + + if packet['to_process'] == 0: + if (packet['command'] & 0xF0) == PUBLISH and packet['qos'] == 0: + with self._callback_mutex: + if self.on_publish: + with self._in_callback_mutex: + try: + self.on_publish( + self, self._userdata, packet['mid']) + except Exception as err: + self._easy_log( + MQTT_LOG_ERR, 'Caught exception in on_publish: %s', err) + + packet['info']._set_as_published() + + if (packet['command'] & 0xF0) == DISCONNECT: + self._current_out_packet_mutex.release() + + with self._msgtime_mutex: + self._last_msg_out = time_func() + + with self._callback_mutex: + if self.on_disconnect: + with self._in_callback_mutex: + try: + self.on_disconnect( + self, self._userdata, 0) + except Exception as err: + self._easy_log( + MQTT_LOG_ERR, 'Caught exception in on_disconnect: %s', err) + + self._sock_close() + return MQTT_ERR_SUCCESS + + with self._out_packet_mutex: + if len(self._out_packet) > 0: + self._current_out_packet = self._out_packet.popleft() + else: + self._current_out_packet = None + else: + break + + self._current_out_packet_mutex.release() + + with self._msgtime_mutex: + self._last_msg_out = time_func() + + return MQTT_ERR_SUCCESS + + def _easy_log(self, level, fmt, *args): + if self.on_log is not None: + buf = fmt % args + try: + self.on_log(self, self._userdata, level, buf) + except Exception: + # Can't _easy_log this, as we'll recurse until we break + pass # self._logger will pick this up, so we're fine + if self._logger is not None: + level_std = LOGGING_LEVEL[level] + self._logger.log(level_std, fmt, *args) + + def _check_keepalive(self): + if self._keepalive == 0: + return MQTT_ERR_SUCCESS + + now = time_func() + + with self._msgtime_mutex: + last_msg_out = self._last_msg_out + last_msg_in = self._last_msg_in + + if self._sock is not None and (now - last_msg_out >= self._keepalive or now - last_msg_in >= self._keepalive): + if self._state == mqtt_cs_connected and self._ping_t == 0: + self._send_pingreq() + with self._msgtime_mutex: + self._last_msg_out = now + self._last_msg_in = now + else: + self._sock_close() + + if self._state == mqtt_cs_disconnecting: + rc = MQTT_ERR_SUCCESS + else: + rc = 1 + with self._callback_mutex: + if self.on_disconnect: + with self._in_callback_mutex: + try: + self.on_disconnect(self, self._userdata, rc) + except Exception as err: + self._easy_log( + MQTT_LOG_ERR, 'Caught exception in on_disconnect: %s', err) + + def _mid_generate(self): + with self._mid_generate_mutex: + self._last_mid += 1 + if self._last_mid == 65536: + self._last_mid = 1 + return self._last_mid + + @staticmethod + def _topic_wildcard_len_check(topic): + # Search for + or # in a topic. Return MQTT_ERR_INVAL if found. + # Also returns MQTT_ERR_INVAL if the topic string is too long. + # Returns MQTT_ERR_SUCCESS if everything is fine. + if b'+' in topic or b'#' in topic or len(topic) > 65535: + return MQTT_ERR_INVAL + else: + return MQTT_ERR_SUCCESS + + @staticmethod + def _filter_wildcard_len_check(sub): + if (len(sub) == 0 or len(sub) > 65535 + or any(b'+' in p or b'#' in p for p in sub.split(b'/') if len(p) > 1) + or b'#/' in sub): + return MQTT_ERR_INVAL + else: + return MQTT_ERR_SUCCESS + + def _send_pingreq(self): + self._easy_log(MQTT_LOG_DEBUG, "Sending PINGREQ") + rc = self._send_simple_command(PINGREQ) + if rc == MQTT_ERR_SUCCESS: + self._ping_t = time_func() + return rc + + def _send_pingresp(self): + self._easy_log(MQTT_LOG_DEBUG, "Sending PINGRESP") + return self._send_simple_command(PINGRESP) + + def _send_puback(self, mid): + self._easy_log(MQTT_LOG_DEBUG, "Sending PUBACK (Mid: %d)", mid) + return self._send_command_with_mid(PUBACK, mid, False) + + def _send_pubcomp(self, mid): + self._easy_log(MQTT_LOG_DEBUG, "Sending PUBCOMP (Mid: %d)", mid) + return self._send_command_with_mid(PUBCOMP, mid, False) + + def _pack_remaining_length(self, packet, remaining_length): + remaining_bytes = [] + while True: + byte = remaining_length % 128 + remaining_length = remaining_length // 128 + # If there are more digits to encode, set the top bit of this digit + if remaining_length > 0: + byte |= 0x80 + + remaining_bytes.append(byte) + packet.append(byte) + if remaining_length == 0: + # FIXME - this doesn't deal with incorrectly large payloads + return packet + + def _pack_str16(self, packet, data): + if isinstance(data, unicode): + data = data.encode('utf-8') + packet.extend(struct.pack("!H", len(data))) + packet.extend(data) + + def _send_publish(self, mid, topic, payload=b'', qos=0, retain=False, dup=False, info=None, properties=None): + # we assume that topic and payload are already properly encoded + assert not isinstance(topic, unicode) and not isinstance( + payload, unicode) and payload is not None + + if self._sock is None: + return MQTT_ERR_NO_CONN + + command = PUBLISH | ((dup & 0x1) << 3) | (qos << 1) | retain + packet = bytearray() + packet.append(command) + + payloadlen = len(payload) + remaining_length = 2 + len(topic) + payloadlen + + if payloadlen == 0: + if self._protocol == MQTTv5: + self._easy_log( + MQTT_LOG_DEBUG, + "Sending PUBLISH (d%d, q%d, r%d, m%d), '%s', properties=%s (NULL payload)", + dup, qos, retain, mid, topic, properties + ) + else: + self._easy_log( + MQTT_LOG_DEBUG, + "Sending PUBLISH (d%d, q%d, r%d, m%d), '%s' (NULL payload)", + dup, qos, retain, mid, topic + ) + else: + if self._protocol == MQTTv5: + self._easy_log( + MQTT_LOG_DEBUG, + "Sending PUBLISH (d%d, q%d, r%d, m%d), '%s', properties=%s, ... (%d bytes)", + dup, qos, retain, mid, topic, properties, payloadlen + ) + else: + self._easy_log( + MQTT_LOG_DEBUG, + "Sending PUBLISH (d%d, q%d, r%d, m%d), '%s', ... (%d bytes)", + dup, qos, retain, mid, topic, payloadlen + ) + + if qos > 0: + # For message id + remaining_length += 2 + + if self._protocol == MQTTv5: + if properties == None: + packed_properties = b'\x00' + else: + packed_properties = properties.pack() + remaining_length += len(packed_properties) + + self._pack_remaining_length(packet, remaining_length) + self._pack_str16(packet, topic) + + if qos > 0: + # For message id + packet.extend(struct.pack("!H", mid)) + + if self._protocol == MQTTv5: + packet.extend(packed_properties) + + packet.extend(payload) + + return self._packet_queue(PUBLISH, packet, mid, qos, info) + + def _send_pubrec(self, mid): + self._easy_log(MQTT_LOG_DEBUG, "Sending PUBREC (Mid: %d)", mid) + return self._send_command_with_mid(PUBREC, mid, False) + + def _send_pubrel(self, mid): + self._easy_log(MQTT_LOG_DEBUG, "Sending PUBREL (Mid: %d)", mid) + return self._send_command_with_mid(PUBREL | 2, mid, False) + + def _send_command_with_mid(self, command, mid, dup): + # For PUBACK, PUBCOMP, PUBREC, and PUBREL + if dup: + command |= 0x8 + + remaining_length = 2 + packet = struct.pack('!BBH', command, remaining_length, mid) + return self._packet_queue(command, packet, mid, 1) + + def _send_simple_command(self, command): + # For DISCONNECT, PINGREQ and PINGRESP + remaining_length = 0 + packet = struct.pack('!BB', command, remaining_length) + return self._packet_queue(command, packet, 0, 0) + + def _send_connect(self, keepalive): + proto_ver = self._protocol + # hard-coded UTF-8 encoded string + protocol = b"MQTT" if proto_ver >= MQTTv311 else b"MQIsdp" + + remaining_length = 2 + len(protocol) + 1 + \ + 1 + 2 + 2 + len(self._client_id) + + connect_flags = 0 + if self._protocol == MQTTv5: + if self._clean_start == True: + connect_flags |= 0x02 + elif self._clean_start == MQTT_CLEAN_START_FIRST_ONLY and self._mqttv5_first_connect == True: + connect_flags |= 0x02 + elif self._clean_session: + connect_flags |= 0x02 + + if self._will: + remaining_length += 2 + \ + len(self._will_topic) + 2 + len(self._will_payload) + connect_flags |= 0x04 | ((self._will_qos & 0x03) << 3) | ( + (self._will_retain & 0x01) << 5) + + if self._username is not None: + remaining_length += 2 + len(self._username) + connect_flags |= 0x80 + if self._password is not None: + connect_flags |= 0x40 + remaining_length += 2 + len(self._password) + + if self._protocol == MQTTv5: + if self._connect_properties == None: + packed_connect_properties = b'\x00' + else: + packed_connect_properties = self._connect_properties.pack() + remaining_length += len(packed_connect_properties) + if self._will: + if self._will_properties == None: + packed_will_properties = b'\x00' + else: + packed_will_properties = self._will_properties.pack() + remaining_length += len(packed_will_properties) + + command = CONNECT + packet = bytearray() + packet.append(command) + + # as per the mosquitto broker, if the MSB of this version is set + # to 1, then it treats the connection as a bridge + if self._client_mode == MQTT_BRIDGE: + proto_ver |= 0x80 + + self._pack_remaining_length(packet, remaining_length) + packet.extend(struct.pack("!H" + str(len(protocol)) + "sBBH", len(protocol), protocol, proto_ver, connect_flags, + keepalive)) + + if self._protocol == MQTTv5: + packet += packed_connect_properties + + self._pack_str16(packet, self._client_id) + + if self._will: + if self._protocol == MQTTv5: + packet += packed_will_properties + self._pack_str16(packet, self._will_topic) + self._pack_str16(packet, self._will_payload) + + if self._username is not None: + self._pack_str16(packet, self._username) + + if self._password is not None: + self._pack_str16(packet, self._password) + + self._keepalive = keepalive + if self._protocol == MQTTv5: + self._easy_log( + MQTT_LOG_DEBUG, + "Sending CONNECT (u%d, p%d, wr%d, wq%d, wf%d, c%d, k%d) client_id=%s properties=%s", + (connect_flags & 0x80) >> 7, + (connect_flags & 0x40) >> 6, + (connect_flags & 0x20) >> 5, + (connect_flags & 0x18) >> 3, + (connect_flags & 0x4) >> 2, + (connect_flags & 0x2) >> 1, + keepalive, + self._client_id, + self._connect_properties + ) + else: + self._easy_log( + MQTT_LOG_DEBUG, + "Sending CONNECT (u%d, p%d, wr%d, wq%d, wf%d, c%d, k%d) client_id=%s", + (connect_flags & 0x80) >> 7, + (connect_flags & 0x40) >> 6, + (connect_flags & 0x20) >> 5, + (connect_flags & 0x18) >> 3, + (connect_flags & 0x4) >> 2, + (connect_flags & 0x2) >> 1, + keepalive, + self._client_id + ) + return self._packet_queue(command, packet, 0, 0) + + def _send_disconnect(self, reasoncode=None, properties=None): + if self._protocol == MQTTv5: + self._easy_log(MQTT_LOG_DEBUG, "Sending DISCONNECT reasonCode=%s properties=%s", + reasoncode, + properties + ) + else: + self._easy_log(MQTT_LOG_DEBUG, "Sending DISCONNECT") + + remaining_length = 0 + + command = DISCONNECT + packet = bytearray() + packet.append(command) + + if self._protocol == MQTTv5: + if properties != None or reasoncode != None: + if reasoncode == None: + reasoncode = ReasonCodes(DISCONNECT >> 4, identifier=0) + remaining_length += 1 + if properties != None: + packed_props = properties.pack() + remaining_length += len(packed_props) + + self._pack_remaining_length(packet, remaining_length) + + if self._protocol == MQTTv5: + if reasoncode != None: + packet += reasoncode.pack() + if properties != None: + packet += packed_props + + return self._packet_queue(command, packet, 0, 0) + + def _send_subscribe(self, dup, topics, properties=None): + remaining_length = 2 + if self._protocol == MQTTv5: + if properties == None: + packed_subscribe_properties = b'\x00' + else: + packed_subscribe_properties = properties.pack() + remaining_length += len(packed_subscribe_properties) + for t, _ in topics: + remaining_length += 2 + len(t) + 1 + + command = SUBSCRIBE | (dup << 3) | 0x2 + packet = bytearray() + packet.append(command) + self._pack_remaining_length(packet, remaining_length) + local_mid = self._mid_generate() + packet.extend(struct.pack("!H", local_mid)) + + if self._protocol == MQTTv5: + packet += packed_subscribe_properties + + for t, q in topics: + self._pack_str16(packet, t) + if self._protocol == MQTTv5: + packet += q.pack() + else: + packet.append(q) + + self._easy_log( + MQTT_LOG_DEBUG, + "Sending SUBSCRIBE (d%d, m%d) %s", + dup, + local_mid, + topics, + ) + return (self._packet_queue(command, packet, local_mid, 1), local_mid) + + def _send_unsubscribe(self, dup, topics, properties=None): + remaining_length = 2 + if self._protocol == MQTTv5: + if properties == None: + packed_unsubscribe_properties = b'\x00' + else: + packed_unsubscribe_properties = properties.pack() + remaining_length += len(packed_unsubscribe_properties) + for t in topics: + remaining_length += 2 + len(t) + + command = UNSUBSCRIBE | (dup << 3) | 0x2 + packet = bytearray() + packet.append(command) + self._pack_remaining_length(packet, remaining_length) + local_mid = self._mid_generate() + packet.extend(struct.pack("!H", local_mid)) + + if self._protocol == MQTTv5: + packet += packed_unsubscribe_properties + + for t in topics: + self._pack_str16(packet, t) + + # topics_repr = ", ".join("'"+topic.decode('utf8')+"'" for topic in topics) + if self._protocol == MQTTv5: + self._easy_log( + MQTT_LOG_DEBUG, + "Sending UNSUBSCRIBE (d%d, m%d) %s %s", + dup, + local_mid, + properties, + topics, + ) + else: + self._easy_log( + MQTT_LOG_DEBUG, + "Sending UNSUBSCRIBE (d%d, m%d) %s", + dup, + local_mid, + topics, + ) + return (self._packet_queue(command, packet, local_mid, 1), local_mid) + + def _message_retry_check_actual(self, messages, mutex): + with mutex: + now = time_func() + for m in messages.values(): + if m.timestamp + self._message_retry < now: + if m.state == mqtt_ms_wait_for_puback or m.state == mqtt_ms_wait_for_pubrec: + m.timestamp = now + m.dup = True + self._send_publish( + m.mid, + m.topic.encode('utf-8'), + m.payload, + m.qos, + m.retain, + m.dup + ) + elif m.state == mqtt_ms_wait_for_pubrel: + m.timestamp = now + self._send_pubrec(m.mid) + elif m.state == mqtt_ms_wait_for_pubcomp: + m.timestamp = now + self._send_pubrel(m.mid) + + def _message_retry_check(self): + self._message_retry_check_actual( + self._out_messages, self._out_message_mutex) + self._message_retry_check_actual( + self._in_messages, self._in_message_mutex) + + def _check_clean_session(self): + if self._protocol == MQTTv5: + if self._clean_start == MQTT_CLEAN_START_FIRST_ONLY: + return self._mqttv5_first_connect + else: + return self._clean_start + else: + return self._clean_session + + def _messages_reconnect_reset_out(self): + with self._out_message_mutex: + self._inflight_messages = 0 + for m in self._out_messages.values(): + m.timestamp = 0 + if self._max_inflight_messages == 0 or self._inflight_messages < self._max_inflight_messages: + if m.qos == 0: + m.state = mqtt_ms_publish + elif m.qos == 1: + # self._inflight_messages = self._inflight_messages + 1 + if m.state == mqtt_ms_wait_for_puback: + m.dup = True + m.state = mqtt_ms_publish + elif m.qos == 2: + # self._inflight_messages = self._inflight_messages + 1 + if self._check_clean_session(): + if m.state != mqtt_ms_publish: + m.dup = True + m.state = mqtt_ms_publish + else: + if m.state == mqtt_ms_wait_for_pubcomp: + m.state = mqtt_ms_resend_pubrel + else: + if m.state == mqtt_ms_wait_for_pubrec: + m.dup = True + m.state = mqtt_ms_publish + else: + m.state = mqtt_ms_queued + + def _messages_reconnect_reset_in(self): + with self._in_message_mutex: + if self._check_clean_session(): + self._in_messages = collections.OrderedDict() + return + for m in self._in_messages.values(): + m.timestamp = 0 + if m.qos != 2: + self._in_messages.pop(m.mid) + else: + # Preserve current state + pass + + def _messages_reconnect_reset(self): + self._messages_reconnect_reset_out() + self._messages_reconnect_reset_in() + + def _packet_queue(self, command, packet, mid, qos, info=None): + mpkt = { + 'command': command, + 'mid': mid, + 'qos': qos, + 'pos': 0, + 'to_process': len(packet), + 'packet': packet, + 'info': info} + + with self._out_packet_mutex: + self._out_packet.append(mpkt) + if self._current_out_packet_mutex.acquire(False): + if self._current_out_packet is None and len(self._out_packet) > 0: + self._current_out_packet = self._out_packet.popleft() + self._current_out_packet_mutex.release() + + # Write a single byte to sockpairW (connected to sockpairR) to break + # out of select() if in threaded mode. + try: + self._sockpairW.send(sockpair_data) + except socket.error as err: + if err.errno != EAGAIN: + raise + + if self._thread is None: + if self._in_callback_mutex.acquire(False): + self._in_callback_mutex.release() + return self.loop_write() + + self._call_socket_register_write() + + return MQTT_ERR_SUCCESS + + def _packet_handle(self): + cmd = self._in_packet['command'] & 0xF0 + if cmd == PINGREQ: + return self._handle_pingreq() + elif cmd == PINGRESP: + return self._handle_pingresp() + elif cmd == PUBACK: + return self._handle_pubackcomp("PUBACK") + elif cmd == PUBCOMP: + return self._handle_pubackcomp("PUBCOMP") + elif cmd == PUBLISH: + return self._handle_publish() + elif cmd == PUBREC: + return self._handle_pubrec() + elif cmd == PUBREL: + return self._handle_pubrel() + elif cmd == CONNACK: + return self._handle_connack() + elif cmd == SUBACK: + return self._handle_suback() + elif cmd == UNSUBACK: + return self._handle_unsuback() + elif cmd == DISCONNECT and self._protocol == MQTTv5: # only allowed in MQTT 5.0 + return self._handle_disconnect() + else: + # If we don't recognise the command, return an error straight away. + self._easy_log(MQTT_LOG_ERR, "Error: Unrecognised command %s", cmd) + return MQTT_ERR_PROTOCOL + + def _handle_pingreq(self): + if self._in_packet['remaining_length'] != 0: + return MQTT_ERR_PROTOCOL + + self._easy_log(MQTT_LOG_DEBUG, "Received PINGREQ") + return self._send_pingresp() + + def _handle_pingresp(self): + if self._in_packet['remaining_length'] != 0: + return MQTT_ERR_PROTOCOL + + # No longer waiting for a PINGRESP. + self._ping_t = 0 + self._easy_log(MQTT_LOG_DEBUG, "Received PINGRESP") + return MQTT_ERR_SUCCESS + + def _handle_connack(self): + if self._protocol == MQTTv5: + if self._in_packet['remaining_length'] < 2: + return MQTT_ERR_PROTOCOL + elif self._in_packet['remaining_length'] != 2: + return MQTT_ERR_PROTOCOL + + if self._protocol == MQTTv5: + (flags, result) = struct.unpack( + "!BB", self._in_packet['packet'][:2]) + reason = ReasonCodes(CONNACK >> 4, identifier=result) + properties = Properties(CONNACK >> 4) + properties.unpack(self._in_packet['packet'][2:]) + else: + (flags, result) = struct.unpack("!BB", self._in_packet['packet']) + if self._protocol == MQTTv311: + if result == CONNACK_REFUSED_PROTOCOL_VERSION: + self._easy_log( + MQTT_LOG_DEBUG, + "Received CONNACK (%s, %s), attempting downgrade to MQTT v3.1.", + flags, result + ) + # Downgrade to MQTT v3.1 + self._protocol = MQTTv31 + return self.reconnect() + elif (result == CONNACK_REFUSED_IDENTIFIER_REJECTED + and self._client_id == b''): + self._easy_log( + MQTT_LOG_DEBUG, + "Received CONNACK (%s, %s), attempting to use non-empty CID", + flags, result, + ) + self._client_id = base62(uuid.uuid4().int, padding=22) + return self.reconnect() + + if result == 0: + self._state = mqtt_cs_connected + self._reconnect_delay = None + + if self._protocol == MQTTv5: + self._easy_log( + MQTT_LOG_DEBUG, "Received CONNACK (%s, %s) properties=%s", flags, reason, properties) + else: + self._easy_log( + MQTT_LOG_DEBUG, "Received CONNACK (%s, %s)", flags, result) + + # it won't be the first successful connect any more + self._mqttv5_first_connect == False + + with self._callback_mutex: + if self.on_connect: + flags_dict = {} + flags_dict['session present'] = flags & 0x01 + with self._in_callback_mutex: + try: + if self._protocol == MQTTv5: + self.on_connect(self, self._userdata, + flags_dict, reason, properties) + else: + self.on_connect( + self, self._userdata, flags_dict, result) + except Exception as err: + self._easy_log( + MQTT_LOG_ERR, 'Caught exception in on_connect: %s', err) + + if result == 0: + rc = 0 + with self._out_message_mutex: + for m in self._out_messages.values(): + m.timestamp = time_func() + if m.state == mqtt_ms_queued: + self.loop_write() # Process outgoing messages that have just been queued up + return MQTT_ERR_SUCCESS + + if m.qos == 0: + with self._in_callback_mutex: # Don't call loop_write after _send_publish() + rc = self._send_publish( + m.mid, + m.topic.encode('utf-8'), + m.payload, + m.qos, + m.retain, + m.dup, + properties=m.properties + ) + if rc != 0: + return rc + elif m.qos == 1: + if m.state == mqtt_ms_publish: + self._inflight_messages += 1 + m.state = mqtt_ms_wait_for_puback + with self._in_callback_mutex: # Don't call loop_write after _send_publish() + rc = self._send_publish( + m.mid, + m.topic.encode('utf-8'), + m.payload, + m.qos, + m.retain, + m.dup, + properties=m.properties + ) + if rc != 0: + return rc + elif m.qos == 2: + if m.state == mqtt_ms_publish: + self._inflight_messages += 1 + m.state = mqtt_ms_wait_for_pubrec + with self._in_callback_mutex: # Don't call loop_write after _send_publish() + rc = self._send_publish( + m.mid, + m.topic.encode('utf-8'), + m.payload, + m.qos, + m.retain, + m.dup, + properties=m.properties + ) + if rc != 0: + return rc + elif m.state == mqtt_ms_resend_pubrel: + self._inflight_messages += 1 + m.state = mqtt_ms_wait_for_pubcomp + with self._in_callback_mutex: # Don't call loop_write after _send_publish() + rc = self._send_pubrel(m.mid) + if rc != 0: + return rc + self.loop_write() # Process outgoing messages that have just been queued up + + return rc + elif result > 0 and result < 6: + return MQTT_ERR_CONN_REFUSED + else: + return MQTT_ERR_PROTOCOL + + def _handle_disconnect(self): + packet_type = DISCONNECT >> 4 + reasonCode = properties = None + if self._in_packet['remaining_length'] > 2: + reasonCode = ReasonCodes(packet_type) + reasonCode.unpack(self._in_packet['packet']) + if self._in_packet['remaining_length'] > 3: + properties = Properties(packet_type) + props, props_len = properties.unpack( + self._in_packet['packet'][1:]) + self._easy_log(MQTT_LOG_DEBUG, "Received DISCONNECT %s %s", + reasonCode, + properties + ) + + self._loop_rc_handle(reasonCode, properties) + + return MQTT_ERR_SUCCESS + + def _handle_suback(self): + self._easy_log(MQTT_LOG_DEBUG, "Received SUBACK") + pack_format = "!H" + str(len(self._in_packet['packet']) - 2) + 's' + (mid, packet) = struct.unpack(pack_format, self._in_packet['packet']) + + if self._protocol == MQTTv5: + properties = Properties(SUBACK >> 4) + props, props_len = properties.unpack(packet) + reasoncodes = [] + for c in packet[props_len:]: + if sys.version_info[0] < 3: + c = ord(c) + reasoncodes.append(ReasonCodes(SUBACK >> 4, identifier=c)) + else: + pack_format = "!" + "B" * len(packet) + granted_qos = struct.unpack(pack_format, packet) + + with self._callback_mutex: + if self.on_subscribe: + with self._in_callback_mutex: # Don't call loop_write after _send_publish() + try: + if self._protocol == MQTTv5: + self.on_subscribe( + self, self._userdata, mid, reasoncodes, properties) + else: + self.on_subscribe( + self, self._userdata, mid, granted_qos) + except Exception as err: + self._easy_log( + MQTT_LOG_ERR, 'Caught exception in on_subscribe: %s', err) + + return MQTT_ERR_SUCCESS + + def _handle_publish(self): + rc = 0 + + header = self._in_packet['command'] + message = MQTTMessage() + message.dup = (header & 0x08) >> 3 + message.qos = (header & 0x06) >> 1 + message.retain = (header & 0x01) + + pack_format = "!H" + str(len(self._in_packet['packet']) - 2) + 's' + (slen, packet) = struct.unpack(pack_format, self._in_packet['packet']) + pack_format = '!' + str(slen) + 's' + str(len(packet) - slen) + 's' + (topic, packet) = struct.unpack(pack_format, packet) + + if self._protocol != MQTTv5 and len(topic) == 0: + return MQTT_ERR_PROTOCOL + + # Handle topics with invalid UTF-8 + # This replaces an invalid topic with a message and the hex + # representation of the topic for logging. When the user attempts to + # access message.topic in the callback, an exception will be raised. + try: + print_topic = topic.decode('utf-8') + except UnicodeDecodeError: + print_topic = "TOPIC WITH INVALID UTF-8: " + str(topic) + + message.topic = topic + + if message.qos > 0: + pack_format = "!H" + str(len(packet) - 2) + 's' + (message.mid, packet) = struct.unpack(pack_format, packet) + + if self._protocol == MQTTv5: + message.properties = Properties(PUBLISH >> 4) + props, props_len = message.properties.unpack(packet) + packet = packet[props_len:] + + message.payload = packet + + if self._protocol == MQTTv5: + self._easy_log( + MQTT_LOG_DEBUG, + "Received PUBLISH (d%d, q%d, r%d, m%d), '%s', properties=%s, ... (%d bytes)", + message.dup, message.qos, message.retain, message.mid, + print_topic, message.properties, len(message.payload) + ) + else: + self._easy_log( + MQTT_LOG_DEBUG, + "Received PUBLISH (d%d, q%d, r%d, m%d), '%s', ... (%d bytes)", + message.dup, message.qos, message.retain, message.mid, + print_topic, len(message.payload) + ) + + message.timestamp = time_func() + if message.qos == 0: + self._handle_on_message(message) + return MQTT_ERR_SUCCESS + elif message.qos == 1: + rc = self._send_puback(message.mid) + self._handle_on_message(message) + return rc + elif message.qos == 2: + rc = self._send_pubrec(message.mid) + message.state = mqtt_ms_wait_for_pubrel + with self._in_message_mutex: + self._in_messages[message.mid] = message + return rc + else: + return MQTT_ERR_PROTOCOL + + def _handle_pubrel(self): + if self._in_packet['remaining_length'] != 2: + return MQTT_ERR_PROTOCOL + + mid, = struct.unpack("!H", self._in_packet['packet']) + self._easy_log(MQTT_LOG_DEBUG, "Received PUBREL (Mid: %d)", mid) + + with self._in_message_mutex: + if mid in self._in_messages: + # Only pass the message on if we have removed it from the queue - this + # prevents multiple callbacks for the same message. + message = self._in_messages.pop(mid) + self._handle_on_message(message) + self._inflight_messages -= 1 + if self._max_inflight_messages > 0: + with self._out_message_mutex: + rc = self._update_inflight() + if rc != MQTT_ERR_SUCCESS: + return rc + + # FIXME: this should only be done if the message is known + # If unknown it's a protocol error and we should close the connection. + # But since we don't have (on disk) persistence for the session, it + # is possible that we must known about this message. + # Choose to acknwoledge this messsage (and thus losing a message) but + # avoid hanging. See #284. + return self._send_pubcomp(mid) + + def _update_inflight(self): + # Dont lock message_mutex here + for m in self._out_messages.values(): + if self._inflight_messages < self._max_inflight_messages: + if m.qos > 0 and m.state == mqtt_ms_queued: + self._inflight_messages += 1 + if m.qos == 1: + m.state = mqtt_ms_wait_for_puback + elif m.qos == 2: + m.state = mqtt_ms_wait_for_pubrec + rc = self._send_publish( + m.mid, + m.topic.encode('utf-8'), + m.payload, + m.qos, + m.retain, + m.dup, + ) + if rc != 0: + return rc + else: + return MQTT_ERR_SUCCESS + return MQTT_ERR_SUCCESS + + def _handle_pubrec(self): + if self._protocol == MQTTv5: + if self._in_packet['remaining_length'] < 2: + return MQTT_ERR_PROTOCOL + elif self._in_packet['remaining_length'] != 2: + return MQTT_ERR_PROTOCOL + + mid, = struct.unpack("!H", self._in_packet['packet'][:2]) + if self._protocol == MQTTv5: + if self._in_packet['remaining_length'] > 2: + reasonCode = ReasonCodes(PUBREC >> 4) + reasonCode.unpack(self._in_packet['packet'][2:]) + if self._in_packet['remaining_length'] > 3: + properties = Properties(PUBREC >> 4) + props, props_len = properties.unpack( + self._in_packet['packet'][3:]) + self._easy_log(MQTT_LOG_DEBUG, "Received PUBREC (Mid: %d)", mid) + + with self._out_message_mutex: + if mid in self._out_messages: + msg = self._out_messages[mid] + msg.state = mqtt_ms_wait_for_pubcomp + msg.timestamp = time_func() + return self._send_pubrel(mid) + + return MQTT_ERR_SUCCESS + + def _handle_unsuback(self): + if self._protocol == MQTTv5: + if self._in_packet['remaining_length'] < 4: + return MQTT_ERR_PROTOCOL + elif self._in_packet['remaining_length'] != 2: + return MQTT_ERR_PROTOCOL + + mid, = struct.unpack("!H", self._in_packet['packet'][:2]) + if self._protocol == MQTTv5: + packet = self._in_packet['packet'][2:] + properties = Properties(UNSUBACK >> 4) + props, props_len = properties.unpack(packet) + reasoncodes = [] + for c in packet[props_len:]: + if sys.version_info[0] < 3: + c = ord(c) + reasoncodes.append(ReasonCodes(UNSUBACK >> 4, identifier=c)) + if len(reasoncodes) == 1: + reasoncodes = reasoncodes[0] + + self._easy_log(MQTT_LOG_DEBUG, "Received UNSUBACK (Mid: %d)", mid) + with self._callback_mutex: + if self.on_unsubscribe: + with self._in_callback_mutex: + try: + if self._protocol == MQTTv5: + self.on_unsubscribe( + self, self._userdata, mid, properties, reasoncodes) + else: + self.on_unsubscribe(self, self._userdata, mid) + except Exception as err: + self._easy_log( + MQTT_LOG_ERR, 'Caught exception in on_unsubscribe: %s', err) + return MQTT_ERR_SUCCESS + + def _do_on_publish(self, mid): + with self._callback_mutex: + if self.on_publish: + with self._in_callback_mutex: + try: + self.on_publish(self, self._userdata, mid) + except Exception as err: + self._easy_log( + MQTT_LOG_ERR, 'Caught exception in on_publish: %s', err) + + msg = self._out_messages.pop(mid) + msg.info._set_as_published() + if msg.qos > 0: + self._inflight_messages -= 1 + if self._max_inflight_messages > 0: + rc = self._update_inflight() + if rc != MQTT_ERR_SUCCESS: + return rc + return MQTT_ERR_SUCCESS + + def _handle_pubackcomp(self, cmd): + if self._protocol == MQTTv5: + if self._in_packet['remaining_length'] < 2: + return MQTT_ERR_PROTOCOL + elif self._in_packet['remaining_length'] != 2: + return MQTT_ERR_PROTOCOL + + packet_type = PUBACK if cmd == "PUBACK" else PUBCOMP + packet_type = packet_type >> 4 + mid, = struct.unpack("!H", self._in_packet['packet'][:2]) + if self._protocol == MQTTv5: + if self._in_packet['remaining_length'] > 2: + reasonCode = ReasonCodes(packet_type) + reasonCode.unpack(self._in_packet['packet'][2:]) + if self._in_packet['remaining_length'] > 3: + properties = Properties(packet_type) + props, props_len = properties.unpack( + self._in_packet['packet'][3:]) + self._easy_log(MQTT_LOG_DEBUG, "Received %s (Mid: %d)", cmd, mid) + + with self._out_message_mutex: + if mid in self._out_messages: + # Only inform the client the message has been sent once. + rc = self._do_on_publish(mid) + return rc + + return MQTT_ERR_SUCCESS + + def _handle_on_message(self, message): + matched = False + with self._callback_mutex: + try: + topic = message.topic + except UnicodeDecodeError: + topic = None + + if topic is not None: + for callback in self._on_message_filtered.iter_match(message.topic): + with self._in_callback_mutex: + callback(self, self._userdata, message) + matched = True + + if matched == False and self.on_message: + with self._in_callback_mutex: + try: + self.on_message(self, self._userdata, message) + except Exception as err: + self._easy_log( + MQTT_LOG_ERR, 'Caught exception in on_message: %s', err) + + def _thread_main(self): + self.loop_forever(retry_first_connection=True) + + def _reconnect_wait(self): + # See reconnect_delay_set for details + now = time_func() + with self._reconnect_delay_mutex: + if self._reconnect_delay is None: + self._reconnect_delay = self._reconnect_min_delay + else: + self._reconnect_delay = min( + self._reconnect_delay * 2, + self._reconnect_max_delay, + ) + + target_time = now + self._reconnect_delay + + remaining = target_time - now + while (self._state != mqtt_cs_disconnecting + and not self._thread_terminate + and remaining > 0): + + time.sleep(min(remaining, 1)) + remaining = target_time - time_func() + + @staticmethod + def _proxy_is_valid(p): + def check(t, a): + return (socks is not None and + t in set([socks.HTTP, socks.SOCKS4, socks.SOCKS5]) and a) + + if isinstance(p, dict): + return check(p.get("proxy_type"), p.get("proxy_addr")) + elif isinstance(p, (list, tuple)): + return len(p) == 6 and check(p[0], p[1]) + else: + return False + + def _get_proxy(self): + if socks is None: + return None + + # First, check if the user explicitly passed us a proxy to use + if self._proxy_is_valid(self._proxy): + return self._proxy + + # Next, check for an mqtt_proxy environment variable as long as the host + # we're trying to connect to isn't listed under the no_proxy environment + # variable (matches built-in module urllib's behavior) + if not (hasattr(urllib_dot_request, "proxy_bypass") and + urllib_dot_request.proxy_bypass(self._host)): + env_proxies = urllib_dot_request.getproxies() + if "mqtt" in env_proxies: + parts = urllib_dot_parse.urlparse(env_proxies["mqtt"]) + if parts.scheme == "http": + proxy = { + "proxy_type": socks.HTTP, + "proxy_addr": parts.hostname, + "proxy_port": parts.port + } + return proxy + elif parts.scheme == "socks": + proxy = { + "proxy_type": socks.SOCKS5, + "proxy_addr": parts.hostname, + "proxy_port": parts.port + } + return proxy + + # Finally, check if the user has monkeypatched the PySocks library with + # a default proxy + socks_default = socks.get_default_proxy() + if self._proxy_is_valid(socks_default): + proxy_keys = ("proxy_type", "proxy_addr", "proxy_port", + "proxy_rdns", "proxy_username", "proxy_password") + return dict(zip(proxy_keys, socks_default)) + + # If we didn't find a proxy through any of the above methods, return + # None to indicate that the connection should be handled normally + return None + + def _create_socket_connection(self): + proxy = self._get_proxy() + addr = (self._host, self._port) + source = (self._bind_address, self._bind_port) + + + if sys.version_info < (2, 7) or (3, 0) < sys.version_info < (3, 2): + # Have to short-circuit here because of unsupported source_address + # param in earlier Python versions. + return socket.create_connection(addr, timeout=self._keepalive) + + if proxy: + return socks.create_connection(addr, source_address=source, timeout=self._keepalive, **proxy) + else: + return socket.create_connection(addr, source_address=source, timeout=self._keepalive) + + +# Compatibility class for easy porting from mosquitto.py. +class Mosquitto(Client): + def __init__(self, client_id="", clean_session=True, userdata=None): + super(Mosquitto, self).__init__(client_id, clean_session, userdata) + + +class WebsocketWrapper(object): + OPCODE_CONTINUATION = 0x0 + OPCODE_TEXT = 0x1 + OPCODE_BINARY = 0x2 + OPCODE_CONNCLOSE = 0x8 + OPCODE_PING = 0x9 + OPCODE_PONG = 0xa + + def __init__(self, socket, host, port, is_ssl, path, extra_headers): + + self.connected = False + + self._ssl = is_ssl + self._host = host + self._port = port + self._socket = socket + self._path = path + + self._sendbuffer = bytearray() + self._readbuffer = bytearray() + + self._requested_size = 0 + self._payload_head = 0 + self._readbuffer_head = 0 + + self._do_handshake(extra_headers) + + def __del__(self): + + self._sendbuffer = None + self._readbuffer = None + + def _do_handshake(self, extra_headers): + + sec_websocket_key = uuid.uuid4().bytes + sec_websocket_key = base64.b64encode(sec_websocket_key) + + websocket_headers = { + "Host": "{self._host:s}:{self._port:d}".format(self=self), + "Upgrade": "websocket", + "Connection": "Upgrade", + "Origin": "https://{self._host:s}:{self._port:d}".format(self=self), + "Sec-WebSocket-Key": sec_websocket_key.decode("utf8"), + "Sec-Websocket-Version": "13", + "Sec-Websocket-Protocol": "mqtt", + } + + # This is checked in ws_set_options so it will either be None, a + # dictionary, or a callable + if isinstance(extra_headers, dict): + websocket_headers.update(extra_headers) + elif callable(extra_headers): + websocket_headers = extra_headers(websocket_headers) + + header = "\r\n".join([ + "GET {self._path} HTTP/1.1".format(self=self), + "\r\n".join("{}: {}".format(i, j) + for i, j in websocket_headers.items()), + "\r\n", + ]).encode("utf8") + + self._socket.send(header) + + has_secret = False + has_upgrade = False + + while True: + # read HTTP response header as lines + byte = self._socket.recv(1) + + self._readbuffer.extend(byte) + + # line end + if byte == b"\n": + if len(self._readbuffer) > 2: + # check upgrade + if b"connection" in str(self._readbuffer).lower().encode('utf-8'): + if b"upgrade" not in str(self._readbuffer).lower().encode('utf-8'): + raise WebsocketConnectionError( + "WebSocket handshake error, connection not upgraded") + else: + has_upgrade = True + + # check key hash + if b"sec-websocket-accept" in str(self._readbuffer).lower().encode('utf-8'): + GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + + server_hash = self._readbuffer.decode( + 'utf-8').split(": ", 1)[1] + server_hash = server_hash.strip().encode('utf-8') + + client_hash = sec_websocket_key.decode('utf-8') + GUID + client_hash = hashlib.sha1(client_hash.encode('utf-8')) + client_hash = base64.b64encode(client_hash.digest()) + + if server_hash != client_hash: + raise WebsocketConnectionError( + "WebSocket handshake error, invalid secret key") + else: + has_secret = True + else: + # ending linebreak + break + + # reset linebuffer + self._readbuffer = bytearray() + + # connection reset + elif not byte: + raise WebsocketConnectionError("WebSocket handshake error") + + if not has_upgrade or not has_secret: + raise WebsocketConnectionError("WebSocket handshake error") + + self._readbuffer = bytearray() + self.connected = True + + def _create_frame(self, opcode, data, do_masking=1): + + header = bytearray() + length = len(data) + + mask_key = bytearray(os.urandom(4)) + mask_flag = do_masking + + # 1 << 7 is the final flag, we don't send continuated data + header.append(1 << 7 | opcode) + + if length < 126: + header.append(mask_flag << 7 | length) + + elif length < 65536: + header.append(mask_flag << 7 | 126) + header += struct.pack("!H", length) + + elif length < 0x8000000000000001: + header.append(mask_flag << 7 | 127) + header += struct.pack("!Q", length) + + else: + raise ValueError("Maximum payload size is 2^63") + + if mask_flag == 1: + for index in range(length): + data[index] ^= mask_key[index % 4] + data = mask_key + data + + return header + data + + def _buffered_read(self, length): + + # try to recv and strore needed bytes + wanted_bytes = length - (len(self._readbuffer) - self._readbuffer_head) + if wanted_bytes > 0: + + data = self._socket.recv(wanted_bytes) + + if not data: + raise socket.error(errno.ECONNABORTED, 0) + else: + self._readbuffer.extend(data) + + if len(data) < wanted_bytes: + raise socket.error(EAGAIN, 0) + + self._readbuffer_head += length + return self._readbuffer[self._readbuffer_head - length:self._readbuffer_head] + + def _recv_impl(self, length): + + # try to decode websocket payload part from data + try: + + self._readbuffer_head = 0 + + result = None + + chunk_startindex = self._payload_head + chunk_endindex = self._payload_head + length + + header1 = self._buffered_read(1) + header2 = self._buffered_read(1) + + opcode = (header1[0] & 0x0f) + maskbit = (header2[0] & 0x80) == 0x80 + lengthbits = (header2[0] & 0x7f) + payload_length = lengthbits + mask_key = None + + # read length + if lengthbits == 0x7e: + + value = self._buffered_read(2) + payload_length, = struct.unpack("!H", value) + + elif lengthbits == 0x7f: + + value = self._buffered_read(8) + payload_length, = struct.unpack("!Q", value) + + # read mask + if maskbit: + mask_key = self._buffered_read(4) + + # if frame payload is shorter than the requested data, read only the possible part + readindex = chunk_endindex + if payload_length < readindex: + readindex = payload_length + + if readindex > 0: + # get payload chunk + payload = self._buffered_read(readindex) + + # unmask only the needed part + if maskbit: + for index in range(chunk_startindex, readindex): + payload[index] ^= mask_key[index % 4] + + result = payload[chunk_startindex:readindex] + self._payload_head = readindex + else: + payload = bytearray() + + # check if full frame arrived and reset readbuffer and payloadhead if needed + if readindex == payload_length: + self._readbuffer = bytearray() + self._payload_head = 0 + + # respond to non-binary opcodes, their arrival is not guaranteed beacause of non-blocking sockets + if opcode == WebsocketWrapper.OPCODE_CONNCLOSE: + frame = self._create_frame( + WebsocketWrapper.OPCODE_CONNCLOSE, payload, 0) + self._socket.send(frame) + + if opcode == WebsocketWrapper.OPCODE_PING: + frame = self._create_frame( + WebsocketWrapper.OPCODE_PONG, payload, 0) + self._socket.send(frame) + + if opcode == WebsocketWrapper.OPCODE_BINARY and payload_length > 0: + return result + else: + raise socket.error(EAGAIN, 0) + + except socket.error as err: + + if err.errno == errno.ECONNABORTED: + self.connected = False + return b'' + else: + # no more data + raise + + def _send_impl(self, data): + + # if previous frame was sent successfully + if len(self._sendbuffer) == 0: + # create websocket frame + frame = self._create_frame( + WebsocketWrapper.OPCODE_BINARY, bytearray(data)) + self._sendbuffer.extend(frame) + self._requested_size = len(data) + + # try to write out as much as possible + length = self._socket.send(self._sendbuffer) + + self._sendbuffer = self._sendbuffer[length:] + + if len(self._sendbuffer) == 0: + # buffer sent out completely, return with payload's size + return self._requested_size + else: + # couldn't send whole data, request the same data again with 0 as sent length + return 0 + + def recv(self, length): + return self._recv_impl(length) + + def read(self, length): + return self._recv_impl(length) + + def send(self, data): + return self._send_impl(data) + + def write(self, data): + return self._send_impl(data) + + def close(self): + self._socket.close() + + def fileno(self): + return self._socket.fileno() + + def pending(self): + # Fix for bug #131: a SSL socket may still have data available + # for reading without select() being aware of it. + if self._ssl: + return self._socket.pending() + else: + # normal socket rely only on select() + return 0 + + def setblocking(self, flag): + self._socket.setblocking(flag) diff --git a/Display/.venv/lib/python3.7/site-packages/paho/mqtt/matcher.py b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/matcher.py new file mode 100644 index 0000000..7fc966a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/matcher.py @@ -0,0 +1,78 @@ +class MQTTMatcher(object): + """Intended to manage topic filters including wildcards. + + Internally, MQTTMatcher use a prefix tree (trie) to store + values associated with filters, and has an iter_match() + method to iterate efficiently over all filters that match + some topic name.""" + + class Node(object): + __slots__ = '_children', '_content' + + def __init__(self): + self._children = {} + self._content = None + + def __init__(self): + self._root = self.Node() + + def __setitem__(self, key, value): + """Add a topic filter :key to the prefix tree + and associate it to :value""" + node = self._root + for sym in key.split('/'): + node = node._children.setdefault(sym, self.Node()) + node._content = value + + def __getitem__(self, key): + """Retrieve the value associated with some topic filter :key""" + try: + node = self._root + for sym in key.split('/'): + node = node._children[sym] + if node._content is None: + raise KeyError(key) + return node._content + except KeyError: + raise KeyError(key) + + def __delitem__(self, key): + """Delete the value associated with some topic filter :key""" + lst = [] + try: + parent, node = None, self._root + for k in key.split('/'): + parent, node = node, node._children[k] + lst.append((parent, k, node)) + # TODO + node._content = None + except KeyError: + raise KeyError(key) + else: # cleanup + for parent, k, node in reversed(lst): + if node._children or node._content is not None: + break + del parent._children[k] + + def iter_match(self, topic): + """Return an iterator on all values associated with filters + that match the :topic""" + lst = topic.split('/') + normal = not topic.startswith('$') + def rec(node, i=0): + if i == len(lst): + if node._content is not None: + yield node._content + else: + part = lst[i] + if part in node._children: + for content in rec(node._children[part], i + 1): + yield content + if '+' in node._children and (normal or i > 0): + for content in rec(node._children['+'], i + 1): + yield content + if '#' in node._children and (normal or i > 0): + content = node._children['#']._content + if content is not None: + yield content + return rec(self._root) diff --git a/Display/.venv/lib/python3.7/site-packages/paho/mqtt/packettypes.py b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/packettypes.py new file mode 100644 index 0000000..7eb4069 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/packettypes.py @@ -0,0 +1,43 @@ +""" +******************************************************************* + Copyright (c) 2017, 2019 IBM Corp. + + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + and Eclipse Distribution License v1.0 which accompany this distribution. + + The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html + and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + + Contributors: + Ian Craggs - initial implementation and/or documentation +******************************************************************* +""" + + +class PacketTypes: + + """ + Packet types class. Includes the AUTH packet for MQTT v5.0. + + Holds constants for each packet type such as PacketTypes.PUBLISH + and packet name strings: PacketTypes.Names[PacketTypes.PUBLISH]. + + """ + + indexes = range(1, 16) + + # Packet types + CONNECT, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL, \ + PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK, \ + PINGREQ, PINGRESP, DISCONNECT, AUTH = indexes + + # Dummy packet type for properties use - will delay only applies to will + WILLMESSAGE = 99 + + Names = [ "reserved", \ + "Connect", "Connack", "Publish", "Puback", "Pubrec", "Pubrel", \ + "Pubcomp", "Subscribe", "Suback", "Unsubscribe", "Unsuback", \ + "Pingreq", "Pingresp", "Disconnect", "Auth"] diff --git a/Display/.venv/lib/python3.7/site-packages/paho/mqtt/properties.py b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/properties.py new file mode 100644 index 0000000..99f654a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/properties.py @@ -0,0 +1,409 @@ +""" +******************************************************************* + Copyright (c) 2017, 2019 IBM Corp. + + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + and Eclipse Distribution License v1.0 which accompany this distribution. + + The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html + and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + + Contributors: + Ian Craggs - initial implementation and/or documentation +******************************************************************* +""" + +import sys, struct + +from .packettypes import PacketTypes + + +class MQTTException(Exception): + pass + + +class MalformedPacket(MQTTException): + pass + + +def writeInt16(length): + # serialize a 16 bit integer to network format + return bytearray(struct.pack("!H", length)) + + +def readInt16(buf): + # deserialize a 16 bit integer from network format + return struct.unpack("!H", buf[:2])[0] + + +def writeInt32(length): + # serialize a 32 bit integer to network format + return bytearray(struct.pack("!L", length)) + + +def readInt32(buf): + # deserialize a 32 bit integer from network format + return struct.unpack("!L", buf[:4])[0] + + +def writeUTF(data): + # data could be a string, or bytes. If string, encode into bytes with utf-8 + if sys.version_info[0] < 3: + data = bytearray(data, 'utf-8') + else: + data = data if type(data) == type(b"") else bytes(data, "utf-8") + return writeInt16(len(data)) + data + + +def readUTF(buffer, maxlen): + if maxlen >= 2: + length = readInt16(buffer) + else: + raise MalformedPacket("Not enough data to read string length") + maxlen -= 2 + if length > maxlen: + raise MalformedPacket("Length delimited string too long") + buf = buffer[2:2+length].decode("utf-8") + # look for chars which are invalid for MQTT + for c in buf: # look for D800-DFFF in the UTF string + ord_c = ord(c) + if ord_c >= 0xD800 and ord_c <= 0xDFFF: + raise MalformedPacket("[MQTT-1.5.4-1] D800-DFFF found in UTF-8 data") + if ord_c == 0x00: # look for null in the UTF string + raise MalformedPacket("[MQTT-1.5.4-2] Null found in UTF-8 data") + if ord_c == 0xFEFF: + raise MalformedPacket("[MQTT-1.5.4-3] U+FEFF in UTF-8 data") + return buf, length+2 + + +def writeBytes(buffer): + return writeInt16(len(buffer)) + buffer + + +def readBytes(buffer): + length = readInt16(buffer) + return buffer[2:2+length], length+2 + + +class VariableByteIntegers: # Variable Byte Integer + """ + MQTT variable byte integer helper class. Used + in several places in MQTT v5.0 properties. + + """ + + @staticmethod + def encode(x): + """ + Convert an integer 0 <= x <= 268435455 into multi-byte format. + Returns the buffer convered from the integer. + """ + assert 0 <= x <= 268435455 + buffer = b'' + while 1: + digit = x % 128 + x //= 128 + if x > 0: + digit |= 0x80 + if sys.version_info[0] >= 3: + buffer += bytes([digit]) + else: + buffer += bytes(chr(digit)) + if x == 0: + break + return buffer + + @staticmethod + def decode(buffer): + """ + Get the value of a multi-byte integer from a buffer + Return the value, and the number of bytes used. + + [MQTT-1.5.5-1] the encoded value MUST use the minimum number of bytes necessary to represent the value + """ + multiplier = 1 + value = 0 + bytes = 0 + while 1: + bytes += 1 + digit = buffer[0] + buffer = buffer[1:] + value += (digit & 127) * multiplier + if digit & 128 == 0: + break + multiplier *= 128 + return (value, bytes) + + +class Properties(object): + """MQTT v5.0 properties class. + + See Properties.names for a list of accepted property names along with their numeric values. + + See Properties.properties for the data type of each property. + + Example of use: + + publish_properties = Properties(PacketTypes.PUBLISH) + publish_properties.UserProperty = ("a", "2") + publish_properties.UserProperty = ("c", "3") + + First the object is created with packet type as argument, no properties will be present at + this point. Then properties are added as attributes, the name of which is the string property + name without the spaces. + + """ + + def __init__(self, packetType): + self.packetType = packetType + self.types = ["Byte", "Two Byte Integer", "Four Byte Integer", "Variable Byte Integer", + "Binary Data", "UTF-8 Encoded String", "UTF-8 String Pair"] + + self.names = { + "Payload Format Indicator": 1, + "Message Expiry Interval": 2, + "Content Type": 3, + "Response Topic": 8, + "Correlation Data": 9, + "Subscription Identifier": 11, + "Session Expiry Interval": 17, + "Assigned Client Identifier": 18, + "Server Keep Alive": 19, + "Authentication Method": 21, + "Authentication Data": 22, + "Request Problem Information": 23, + "Will Delay Interval": 24, + "Request Response Information": 25, + "Response Information": 26, + "Server Reference": 28, + "Reason String": 31, + "Receive Maximum": 33, + "Topic Alias Maximum": 34, + "Topic Alias": 35, + "Maximum QoS": 36, + "Retain Available": 37, + "User Property": 38, + "Maximum Packet Size": 39, + "Wildcard Subscription Available": 40, + "Subscription Identifier Available": 41, + "Shared Subscription Available": 42 + } + + self.properties = { + # id: type, packets + # payload format indicator + 1: (self.types.index("Byte"), [PacketTypes.PUBLISH, PacketTypes.WILLMESSAGE]), + 2: (self.types.index("Four Byte Integer"), [PacketTypes.PUBLISH, PacketTypes.WILLMESSAGE]), + 3: (self.types.index("UTF-8 Encoded String"), [PacketTypes.PUBLISH, PacketTypes.WILLMESSAGE]), + 8: (self.types.index("UTF-8 Encoded String"), [PacketTypes.PUBLISH, PacketTypes.WILLMESSAGE]), + 9: (self.types.index("Binary Data"), [PacketTypes.PUBLISH, PacketTypes.WILLMESSAGE]), + 11: (self.types.index("Variable Byte Integer"), + [PacketTypes.PUBLISH, PacketTypes.SUBSCRIBE]), + 17: (self.types.index("Four Byte Integer"), + [PacketTypes.CONNECT, PacketTypes.CONNACK, PacketTypes.DISCONNECT]), + 18: (self.types.index("UTF-8 Encoded String"), [PacketTypes.CONNACK]), + 19: (self.types.index("Two Byte Integer"), [PacketTypes.CONNACK]), + 21: (self.types.index("UTF-8 Encoded String"), + [PacketTypes.CONNECT, PacketTypes.CONNACK, PacketTypes.AUTH]), + 22: (self.types.index("Binary Data"), + [PacketTypes.CONNECT, PacketTypes.CONNACK, PacketTypes.AUTH]), + 23: (self.types.index("Byte"), + [PacketTypes.CONNECT]), + 24: (self.types.index("Four Byte Integer"), [PacketTypes.WILLMESSAGE]), + 25: (self.types.index("Byte"), [PacketTypes.CONNECT]), + 26: (self.types.index("UTF-8 Encoded String"), [PacketTypes.CONNACK]), + 28: (self.types.index("UTF-8 Encoded String"), + [PacketTypes.CONNACK, PacketTypes.DISCONNECT]), + 31: (self.types.index("UTF-8 Encoded String"), + [PacketTypes.CONNACK, PacketTypes.PUBACK, PacketTypes.PUBREC, + PacketTypes.PUBREL, PacketTypes.PUBCOMP, PacketTypes.SUBACK, + PacketTypes.UNSUBACK, PacketTypes.DISCONNECT, PacketTypes.AUTH]), + 33: (self.types.index("Two Byte Integer"), + [PacketTypes.CONNECT, PacketTypes.CONNACK]), + 34: (self.types.index("Two Byte Integer"), + [PacketTypes.CONNECT, PacketTypes.CONNACK]), + 35: (self.types.index("Two Byte Integer"), [PacketTypes.PUBLISH]), + 36: (self.types.index("Byte"), [PacketTypes.CONNACK]), + 37: (self.types.index("Byte"), [PacketTypes.CONNACK]), + 38: (self.types.index("UTF-8 String Pair"), + [PacketTypes.CONNECT, PacketTypes.CONNACK, + PacketTypes.PUBLISH, PacketTypes.PUBACK, + PacketTypes.PUBREC, PacketTypes.PUBREL, PacketTypes.PUBCOMP, + PacketTypes.SUBSCRIBE, PacketTypes.SUBACK, + PacketTypes.UNSUBSCRIBE, PacketTypes.UNSUBACK, + PacketTypes.DISCONNECT, PacketTypes.AUTH, PacketTypes.WILLMESSAGE]), + 39: (self.types.index("Four Byte Integer"), + [PacketTypes.CONNECT, PacketTypes.CONNACK]), + 40: (self.types.index("Byte"), [PacketTypes.CONNACK]), + 41: (self.types.index("Byte"), [PacketTypes.CONNACK]), + 42: (self.types.index("Byte"), [PacketTypes.CONNACK]), + } + + def allowsMultiple(self, compressedName): + return self.getIdentFromName(compressedName) in [11, 38] + + def getIdentFromName(self, compressedName): + # return the identifier corresponding to the property name + result = -1 + for name in self.names.keys(): + if compressedName == name.replace(' ', ''): + result = self.names[name] + break + return result + + def __setattr__(self, name, value): + name = name.replace(' ', '') + privateVars = ["packetType", "types", "names", "properties"] + if name in privateVars: + object.__setattr__(self, name, value) + else: + # the name could have spaces in, or not. Remove spaces before assignment + if name not in [aname.replace(' ', '') for aname in self.names.keys()]: + raise MQTTException( + "Property name must be one of "+str(self.names.keys())) + # check that this attribute applies to the packet type + if self.packetType not in self.properties[self.getIdentFromName(name)][1]: + raise MQTTException("Property %s does not apply to packet type %s" + % (name, PacketTypes.Names[self.packetType])) + if self.allowsMultiple(name): + if type(value) != type([]): + value = [value] + if hasattr(self, name): + value = object.__getattribute__(self, name) + value + object.__setattr__(self, name, value) + + def __str__(self): + buffer = "[" + first = True + for name in self.names.keys(): + compressedName = name.replace(' ', '') + if hasattr(self, compressedName): + if not first: + buffer += ", " + buffer += compressedName + " : " + \ + str(getattr(self, compressedName)) + first = False + buffer += "]" + return buffer + + def json(self): + data = {} + for name in self.names.keys(): + compressedName = name.replace(' ', '') + if hasattr(self, compressedName): + data[compressedName] = getattr(self, compressedName) + return data + + def isEmpty(self): + rc = True + for name in self.names.keys(): + compressedName = name.replace(' ', '') + if hasattr(self, compressedName): + rc = False + break + return rc + + def clear(self): + for name in self.names.keys(): + compressedName = name.replace(' ', '') + if hasattr(self, compressedName): + delattr(self, compressedName) + + def writeProperty(self, identifier, type, value): + buffer = b"" + buffer += VariableByteIntegers.encode(identifier) # identifier + if type == self.types.index("Byte"): # value + if sys.version_info[0] < 3: + buffer += chr(value) + else: + buffer += bytes([value]) + elif type == self.types.index("Two Byte Integer"): + buffer += writeInt16(value) + elif type == self.types.index("Four Byte Integer"): + buffer += writeInt32(value) + elif type == self.types.index("Variable Byte Integer"): + buffer += VariableByteIntegers.encode(value) + elif type == self.types.index("Binary Data"): + buffer += writeBytes(value) + elif type == self.types.index("UTF-8 Encoded String"): + buffer += writeUTF(value) + elif type == self.types.index("UTF-8 String Pair"): + buffer += writeUTF(value[0]) + writeUTF(value[1]) + return buffer + + def pack(self): + # serialize properties into buffer for sending over network + buffer = b"" + for name in self.names.keys(): + compressedName = name.replace(' ', '') + if hasattr(self, compressedName): + identifier = self.getIdentFromName(compressedName) + attr_type = self.properties[identifier][0] + if self.allowsMultiple(compressedName): + for prop in getattr(self, compressedName): + buffer += self.writeProperty(identifier, + attr_type, prop) + else: + buffer += self.writeProperty(identifier, attr_type, + getattr(self, compressedName)) + return VariableByteIntegers.encode(len(buffer)) + buffer + + def readProperty(self, buffer, type, propslen): + if type == self.types.index("Byte"): + value = buffer[0] + valuelen = 1 + elif type == self.types.index("Two Byte Integer"): + value = readInt16(buffer) + valuelen = 2 + elif type == self.types.index("Four Byte Integer"): + value = readInt32(buffer) + valuelen = 4 + elif type == self.types.index("Variable Byte Integer"): + value, valuelen = VariableByteIntegers.decode(buffer) + elif type == self.types.index("Binary Data"): + value, valuelen = readBytes(buffer) + elif type == self.types.index("UTF-8 Encoded String"): + value, valuelen = readUTF(buffer, propslen) + elif type == self.types.index("UTF-8 String Pair"): + value, valuelen = readUTF(buffer, propslen) + buffer = buffer[valuelen:] # strip the bytes used by the value + value1, valuelen1 = readUTF(buffer, propslen - valuelen) + value = (value, value1) + valuelen += valuelen1 + return value, valuelen + + def getNameFromIdent(self, identifier): + rc = None + for name in self.names: + if self.names[name] == identifier: + rc = name + return rc + + def unpack(self, buffer): + if sys.version_info[0] < 3: + buffer = bytearray(buffer) + self.clear() + # deserialize properties into attributes from buffer received from network + propslen, VBIlen = VariableByteIntegers.decode(buffer) + buffer = buffer[VBIlen:] # strip the bytes used by the VBI + propslenleft = propslen + while propslenleft > 0: # properties length is 0 if there are none + identifier, VBIlen = VariableByteIntegers.decode( + buffer) # property identifier + buffer = buffer[VBIlen:] # strip the bytes used by the VBI + propslenleft -= VBIlen + attr_type = self.properties[identifier][0] + value, valuelen = self.readProperty( + buffer, attr_type, propslenleft) + buffer = buffer[valuelen:] # strip the bytes used by the value + propslenleft -= valuelen + propname = self.getNameFromIdent(identifier) + compressedName = propname.replace(' ', '') + if not self.allowsMultiple(compressedName) and hasattr(self, compressedName): + raise MQTTException( + "Property '%s' must not exist more than once" % property) + setattr(self, propname, value) + return self, propslen + VBIlen diff --git a/Display/.venv/lib/python3.7/site-packages/paho/mqtt/publish.py b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/publish.py new file mode 100644 index 0000000..f9f1986 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/publish.py @@ -0,0 +1,228 @@ +# Copyright (c) 2014 Roger Light +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# and Eclipse Distribution License v1.0 which accompany this distribution. +# +# The Eclipse Public License is available at +# http://www.eclipse.org/legal/epl-v10.html +# and the Eclipse Distribution License is available at +# http://www.eclipse.org/org/documents/edl-v10.php. +# +# Contributors: +# Roger Light - initial API and implementation + +""" +This module provides some helper functions to allow straightforward publishing +of messages in a one-shot manner. In other words, they are useful for the +situation where you have a single/multiple messages you want to publish to a +broker, then disconnect and nothing else is required. +""" +from __future__ import absolute_import + +import collections + +from . import client as paho +from .. import mqtt + +def _do_publish(client): + """Internal function""" + + message = client._userdata.popleft() + + if isinstance(message, dict): + client.publish(**message) + elif isinstance(message, (tuple, list)): + client.publish(*message) + else: + raise TypeError('message must be a dict, tuple, or list') + + +def _on_connect(client, userdata, flags, rc): + """Internal callback""" + #pylint: disable=invalid-name, unused-argument + + if rc == 0: + if len(userdata) > 0: + _do_publish(client) + else: + raise mqtt.MQTTException(paho.connack_string(rc)) + + +def _on_publish(client, userdata, mid): + """Internal callback""" + #pylint: disable=unused-argument + + if len(userdata) == 0: + client.disconnect() + else: + _do_publish(client) + + +def multiple(msgs, hostname="localhost", port=1883, client_id="", keepalive=60, + will=None, auth=None, tls=None, protocol=paho.MQTTv311, + transport="tcp", proxy_args=None): + """Publish multiple messages to a broker, then disconnect cleanly. + + This function creates an MQTT client, connects to a broker and publishes a + list of messages. Once the messages have been delivered, it disconnects + cleanly from the broker. + + msgs : a list of messages to publish. Each message is either a dict or a + tuple. + + If a dict, only the topic must be present. Default values will be + used for any missing arguments. The dict must be of the form: + + msg = {'topic':"", 'payload':"", 'qos':, + 'retain':} + topic must be present and may not be empty. + If payload is "", None or not present then a zero length payload + will be published. + If qos is not present, the default of 0 is used. + If retain is not present, the default of False is used. + + If a tuple, then it must be of the form: + ("", "", qos, retain) + + hostname : a string containing the address of the broker to connect to. + Defaults to localhost. + + port : the port to connect to the broker on. Defaults to 1883. + + client_id : the MQTT client id to use. If "" or None, the Paho library will + generate a client id automatically. + + keepalive : the keepalive timeout value for the client. Defaults to 60 + seconds. + + will : a dict containing will parameters for the client: will = {'topic': + "", 'payload':", 'qos':, 'retain':}. + Topic is required, all other parameters are optional and will + default to None, 0 and False respectively. + Defaults to None, which indicates no will should be used. + + auth : a dict containing authentication parameters for the client: + auth = {'username':"", 'password':""} + Username is required, password is optional and will default to None + if not provided. + Defaults to None, which indicates no authentication is to be used. + + tls : a dict containing TLS configuration parameters for the client: + dict = {'ca_certs':"", 'certfile':"", + 'keyfile':"", 'tls_version':"", + 'ciphers':", 'insecure':""} + ca_certs is required, all other parameters are optional and will + default to None if not provided, which results in the client using + the default behaviour - see the paho.mqtt.client documentation. + Alternatively, tls input can be an SSLContext object, which will be + processed using the tls_set_context method. + Defaults to None, which indicates that TLS should not be used. + + transport : set to "tcp" to use the default setting of transport which is + raw TCP. Set to "websockets" to use WebSockets as the transport. + proxy_args: a dictionary that will be given to the client. + """ + + if not isinstance(msgs, collections.Iterable): + raise TypeError('msgs must be an iterable') + + client = paho.Client(client_id=client_id, userdata=collections.deque(msgs), + protocol=protocol, transport=transport) + + client.on_publish = _on_publish + client.on_connect = _on_connect + + if proxy_args is not None: + client.proxy_set(**proxy_args) + + if auth: + username = auth.get('username') + if username: + password = auth.get('password') + client.username_pw_set(username, password) + else: + raise KeyError("The 'username' key was not found, this is " + "required for auth") + + if will is not None: + client.will_set(**will) + + if tls is not None: + if isinstance(tls, dict): + insecure = tls.pop('insecure', False) + client.tls_set(**tls) + if insecure: + # Must be set *after* the `client.tls_set()` call since it sets + # up the SSL context that `client.tls_insecure_set` alters. + client.tls_insecure_set(insecure) + else: + # Assume input is SSLContext object + client.tls_set_context(tls) + + client.connect(hostname, port, keepalive) + client.loop_forever() + + +def single(topic, payload=None, qos=0, retain=False, hostname="localhost", + port=1883, client_id="", keepalive=60, will=None, auth=None, + tls=None, protocol=paho.MQTTv311, transport="tcp", proxy_args=None): + """Publish a single message to a broker, then disconnect cleanly. + + This function creates an MQTT client, connects to a broker and publishes a + single message. Once the message has been delivered, it disconnects cleanly + from the broker. + + topic : the only required argument must be the topic string to which the + payload will be published. + + payload : the payload to be published. If "" or None, a zero length payload + will be published. + + qos : the qos to use when publishing, default to 0. + + retain : set the message to be retained (True) or not (False). + + hostname : a string containing the address of the broker to connect to. + Defaults to localhost. + + port : the port to connect to the broker on. Defaults to 1883. + + client_id : the MQTT client id to use. If "" or None, the Paho library will + generate a client id automatically. + + keepalive : the keepalive timeout value for the client. Defaults to 60 + seconds. + + will : a dict containing will parameters for the client: will = {'topic': + "", 'payload':", 'qos':, 'retain':}. + Topic is required, all other parameters are optional and will + default to None, 0 and False respectively. + Defaults to None, which indicates no will should be used. + + auth : a dict containing authentication parameters for the client: + auth = {'username':"", 'password':""} + Username is required, password is optional and will default to None + if not provided. + Defaults to None, which indicates no authentication is to be used. + + tls : a dict containing TLS configuration parameters for the client: + dict = {'ca_certs':"", 'certfile':"", + 'keyfile':"", 'tls_version':"", + 'ciphers':", 'insecure':""} + ca_certs is required, all other parameters are optional and will + default to None if not provided, which results in the client using + the default behaviour - see the paho.mqtt.client documentation. + Defaults to None, which indicates that TLS should not be used. + Alternatively, tls input can be an SSLContext object, which will be + processed using the tls_set_context method. + + transport : set to "tcp" to use the default setting of transport which is + raw TCP. Set to "websockets" to use WebSockets as the transport. + proxy_args: a dictionary that will be given to the client. + """ + + msg = {'topic':topic, 'payload':payload, 'qos':qos, 'retain':retain} + + multiple([msg], hostname, port, client_id, keepalive, will, auth, tls, + protocol, transport, proxy_args) diff --git a/Display/.venv/lib/python3.7/site-packages/paho/mqtt/reasoncodes.py b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/reasoncodes.py new file mode 100644 index 0000000..12325bc --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/reasoncodes.py @@ -0,0 +1,191 @@ +""" +******************************************************************* + Copyright (c) 2017, 2019 IBM Corp. + + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + and Eclipse Distribution License v1.0 which accompany this distribution. + + The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html + and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + + Contributors: + Ian Craggs - initial implementation and/or documentation +******************************************************************* +""" + +import sys +from .packettypes import PacketTypes + + +class ReasonCodes: + """MQTT version 5.0 reason codes class. + + See ReasonCodes.names for a list of possible numeric values along with their + names and the packets to which they apply. + + """ + + def __init__(self, packetType, aName="Success", identifier=-1): + """ + packetType: the type of the packet, such as PacketTypes.CONNECT that + this reason code will be used with. Some reason codes have different + names for the same identifier when used a different packet type. + + aName: the String name of the reason code to be created. Ignored + if the identifier is set. + + identifier: an integer value of the reason code to be created. + + """ + + self.packetType = packetType + self.names = { + 0: {"Success": [PacketTypes.CONNACK, PacketTypes.PUBACK, + PacketTypes.PUBREC, PacketTypes.PUBREL, PacketTypes.PUBCOMP, + PacketTypes.UNSUBACK, PacketTypes.AUTH], + "Normal disconnection": [PacketTypes.DISCONNECT], + "Granted QoS 0": [PacketTypes.SUBACK]}, + 1: {"Granted QoS 1": [PacketTypes.SUBACK]}, + 2: {"Granted QoS 2": [PacketTypes.SUBACK]}, + 4: {"Disconnect with will message": [PacketTypes.DISCONNECT]}, + 16: {"No matching subscribers": + [PacketTypes.PUBACK, PacketTypes.PUBREC]}, + 17: {"No subscription found": [PacketTypes.UNSUBACK]}, + 24: {"Continue authentication": [PacketTypes.AUTH]}, + 25: {"Re-authenticate": [PacketTypes.AUTH]}, + 128: {"Unspecified error": [PacketTypes.CONNACK, PacketTypes.PUBACK, + PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.UNSUBACK, + PacketTypes.DISCONNECT], }, + 129: {"Malformed packet": + [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 130: {"Protocol error": + [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 131: {"Implementation specific error": [PacketTypes.CONNACK, + PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.SUBACK, + PacketTypes.UNSUBACK, PacketTypes.DISCONNECT], }, + 132: {"Unsupported protocol version": [PacketTypes.CONNACK]}, + 133: {"Client identifier not valid": [PacketTypes.CONNACK]}, + 134: {"Bad user name or password": [PacketTypes.CONNACK]}, + 135: {"Not authorized": [PacketTypes.CONNACK, PacketTypes.PUBACK, + PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.UNSUBACK, + PacketTypes.DISCONNECT], }, + 136: {"Server unavailable": [PacketTypes.CONNACK]}, + 137: {"Server busy": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 138: {"Banned": [PacketTypes.CONNACK]}, + 139: {"Server shutting down": [PacketTypes.DISCONNECT]}, + 140: {"Bad authentication method": + [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 141: {"Keep alive timeout": [PacketTypes.DISCONNECT]}, + 142: {"Session taken over": [PacketTypes.DISCONNECT]}, + 143: {"Topic filter invalid": + [PacketTypes.SUBACK, PacketTypes.UNSUBACK, PacketTypes.DISCONNECT]}, + 144: {"Topic name invalid": + [PacketTypes.CONNACK, PacketTypes.PUBACK, + PacketTypes.PUBREC, PacketTypes.DISCONNECT]}, + 145: {"Packet identifier in use": + [PacketTypes.PUBACK, PacketTypes.PUBREC, + PacketTypes.SUBACK, PacketTypes.UNSUBACK]}, + 146: {"Packet identifier not found": + [PacketTypes.PUBREL, PacketTypes.PUBCOMP]}, + 147: {"Receive maximum exceeded": [PacketTypes.DISCONNECT]}, + 148: {"Topic alias invalid": [PacketTypes.DISCONNECT]}, + 149: {"Packet too large": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 150: {"Message rate too high": [PacketTypes.DISCONNECT]}, + 151: {"Quota exceeded": [PacketTypes.CONNACK, PacketTypes.PUBACK, + PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.DISCONNECT], }, + 152: {"Administrative action": [PacketTypes.DISCONNECT]}, + 153: {"Payload format invalid": + [PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.DISCONNECT]}, + 154: {"Retain not supported": + [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 155: {"QoS not supported": + [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 156: {"Use another server": + [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 157: {"Server moved": + [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 158: {"Shared subscription not supported": + [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, + 159: {"Connection rate exceeded": + [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 160: {"Maximum connect time": + [PacketTypes.DISCONNECT]}, + 161: {"Subscription identifiers not supported": + [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, + 162: {"Wildcard subscription not supported": + [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, + } + if identifier == -1: + if packetType == PacketTypes.DISCONNECT and aName == "Success": + aName = "Normal disconnection" + self.set(aName) + else: + self.value = identifier + self.getName() # check it's good + + def __getName__(self, packetType, identifier): + """ + Get the reason code string name for a specific identifier. + The name can vary by packet type for the same identifier, which + is why the packet type is also required. + + Used when displaying the reason code. + """ + assert identifier in self.names.keys(), identifier + names = self.names[identifier] + namelist = [name for name in names.keys() if packetType in names[name]] + assert len(namelist) == 1 + return namelist[0] + + def getId(self, name): + """ + Get the numeric id corresponding to a reason code name. + + Used when setting the reason code for a packetType + check that only valid codes for the packet are set. + """ + identifier = None + for code in self.names.keys(): + if name in self.names[code].keys(): + if self.packetType in self.names[code][name]: + identifier = code + break + assert identifier != None, name + return identifier + + def set(self, name): + self.value = self.getId(name) + + def unpack(self, buffer): + c = buffer[0] + if sys.version_info[0] < 3: + c = ord(c) + name = self.__getName__(self.packetType, c) + self.value = self.getId(name) + return 1 + + def getName(self): + """Returns the reason code name corresponding to the numeric value which is set. + """ + return self.__getName__(self.packetType, self.value) + + def __eq__(self, other): + if isinstance(other, int): + return self.value == other + if isinstance(other, str): + return self.value == str(self) + if isinstance(other, ReasonCodes): + return self.value == other.value + return False + + def __str__(self): + return self.getName() + + def json(self): + return self.getName() + + def pack(self): + return bytearray([self.value]) \ No newline at end of file diff --git a/Display/.venv/lib/python3.7/site-packages/paho/mqtt/subscribe.py b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/subscribe.py new file mode 100644 index 0000000..8900a6b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/subscribe.py @@ -0,0 +1,266 @@ +# Copyright (c) 2016 Roger Light +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# and Eclipse Distribution License v1.0 which accompany this distribution. +# +# The Eclipse Public License is available at +# http://www.eclipse.org/legal/epl-v10.html +# and the Eclipse Distribution License is available at +# http://www.eclipse.org/org/documents/edl-v10.php. +# +# Contributors: +# Roger Light - initial API and implementation + +""" +This module provides some helper functions to allow straightforward subscribing +to topics and retrieving messages. The two functions are simple(), which +returns one or messages matching a set of topics, and callback() which allows +you to pass a callback for processing of messages. +""" +from __future__ import absolute_import + +from . import client as paho +from .. import mqtt + +def _on_connect(client, userdata, flags, rc): + """Internal callback""" + if rc != 0: + raise mqtt.MQTTException(paho.connack_string(rc)) + + if isinstance(userdata['topics'], list): + for topic in userdata['topics']: + client.subscribe(topic, userdata['qos']) + else: + client.subscribe(userdata['topics'], userdata['qos']) + + +def _on_message_callback(client, userdata, message): + """Internal callback""" + userdata['callback'](client, userdata['userdata'], message) + + +def _on_message_simple(client, userdata, message): + """Internal callback""" + + if userdata['msg_count'] == 0: + return + + # Don't process stale retained messages if 'retained' was false + if message.retain and not userdata['retained']: + return + + userdata['msg_count'] = userdata['msg_count'] - 1 + + if userdata['messages'] is None and userdata['msg_count'] == 0: + userdata['messages'] = message + client.disconnect() + return + + userdata['messages'].append(message) + if userdata['msg_count'] == 0: + client.disconnect() + + +def callback(callback, topics, qos=0, userdata=None, hostname="localhost", + port=1883, client_id="", keepalive=60, will=None, auth=None, + tls=None, protocol=paho.MQTTv311, transport="tcp", + clean_session=True, proxy_args=None): + """Subscribe to a list of topics and process them in a callback function. + + This function creates an MQTT client, connects to a broker and subscribes + to a list of topics. Incoming messages are processed by the user provided + callback. This is a blocking function and will never return. + + callback : function of the form "on_message(client, userdata, message)" for + processing the messages received. + + topics : either a string containing a single topic to subscribe to, or a + list of topics to subscribe to. + + qos : the qos to use when subscribing. This is applied to all topics. + + userdata : passed to the callback + + hostname : a string containing the address of the broker to connect to. + Defaults to localhost. + + port : the port to connect to the broker on. Defaults to 1883. + + client_id : the MQTT client id to use. If "" or None, the Paho library will + generate a client id automatically. + + keepalive : the keepalive timeout value for the client. Defaults to 60 + seconds. + + will : a dict containing will parameters for the client: will = {'topic': + "", 'payload':", 'qos':, 'retain':}. + Topic is required, all other parameters are optional and will + default to None, 0 and False respectively. + Defaults to None, which indicates no will should be used. + + auth : a dict containing authentication parameters for the client: + auth = {'username':"", 'password':""} + Username is required, password is optional and will default to None + if not provided. + Defaults to None, which indicates no authentication is to be used. + + tls : a dict containing TLS configuration parameters for the client: + dict = {'ca_certs':"", 'certfile':"", + 'keyfile':"", 'tls_version':"", + 'ciphers':", 'insecure':""} + ca_certs is required, all other parameters are optional and will + default to None if not provided, which results in the client using + the default behaviour - see the paho.mqtt.client documentation. + Alternatively, tls input can be an SSLContext object, which will be + processed using the tls_set_context method. + Defaults to None, which indicates that TLS should not be used. + + transport : set to "tcp" to use the default setting of transport which is + raw TCP. Set to "websockets" to use WebSockets as the transport. + + clean_session : a boolean that determines the client type. If True, + the broker will remove all information about this client + when it disconnects. If False, the client is a persistent + client and subscription information and queued messages + will be retained when the client disconnects. + Defaults to True. + + proxy_args: a dictionary that will be given to the client. + """ + + if qos < 0 or qos > 2: + raise ValueError('qos must be in the range 0-2') + + callback_userdata = { + 'callback':callback, + 'topics':topics, + 'qos':qos, + 'userdata':userdata} + + client = paho.Client(client_id=client_id, userdata=callback_userdata, + protocol=protocol, transport=transport, + clean_session=clean_session) + client.on_message = _on_message_callback + client.on_connect = _on_connect + + if proxy_args is not None: + client.proxy_set(**proxy_args) + + if auth: + username = auth.get('username') + if username: + password = auth.get('password') + client.username_pw_set(username, password) + else: + raise KeyError("The 'username' key was not found, this is " + "required for auth") + + if will is not None: + client.will_set(**will) + + if tls is not None: + if isinstance(tls, dict): + insecure = tls.pop('insecure', False) + client.tls_set(**tls) + if insecure: + # Must be set *after* the `client.tls_set()` call since it sets + # up the SSL context that `client.tls_insecure_set` alters. + client.tls_insecure_set(insecure) + else: + # Assume input is SSLContext object + client.tls_set_context(tls) + + client.connect(hostname, port, keepalive) + client.loop_forever() + + +def simple(topics, qos=0, msg_count=1, retained=True, hostname="localhost", + port=1883, client_id="", keepalive=60, will=None, auth=None, + tls=None, protocol=paho.MQTTv311, transport="tcp", + clean_session=True, proxy_args=None): + """Subscribe to a list of topics and return msg_count messages. + + This function creates an MQTT client, connects to a broker and subscribes + to a list of topics. Once "msg_count" messages have been received, it + disconnects cleanly from the broker and returns the messages. + + topics : either a string containing a single topic to subscribe to, or a + list of topics to subscribe to. + + qos : the qos to use when subscribing. This is applied to all topics. + + msg_count : the number of messages to retrieve from the broker. + if msg_count == 1 then a single MQTTMessage will be returned. + if msg_count > 1 then a list of MQTTMessages will be returned. + + retained : If set to True, retained messages will be processed the same as + non-retained messages. If set to False, retained messages will + be ignored. This means that with retained=False and msg_count=1, + the function will return the first message received that does + not have the retained flag set. + + hostname : a string containing the address of the broker to connect to. + Defaults to localhost. + + port : the port to connect to the broker on. Defaults to 1883. + + client_id : the MQTT client id to use. If "" or None, the Paho library will + generate a client id automatically. + + keepalive : the keepalive timeout value for the client. Defaults to 60 + seconds. + + will : a dict containing will parameters for the client: will = {'topic': + "", 'payload':", 'qos':, 'retain':}. + Topic is required, all other parameters are optional and will + default to None, 0 and False respectively. + Defaults to None, which indicates no will should be used. + + auth : a dict containing authentication parameters for the client: + auth = {'username':"", 'password':""} + Username is required, password is optional and will default to None + if not provided. + Defaults to None, which indicates no authentication is to be used. + + tls : a dict containing TLS configuration parameters for the client: + dict = {'ca_certs':"", 'certfile':"", + 'keyfile':"", 'tls_version':"", + 'ciphers':", 'insecure':""} + ca_certs is required, all other parameters are optional and will + default to None if not provided, which results in the client using + the default behaviour - see the paho.mqtt.client documentation. + Alternatively, tls input can be an SSLContext object, which will be + processed using the tls_set_context method. + Defaults to None, which indicates that TLS should not be used. + + transport : set to "tcp" to use the default setting of transport which is + raw TCP. Set to "websockets" to use WebSockets as the transport. + + clean_session : a boolean that determines the client type. If True, + the broker will remove all information about this client + when it disconnects. If False, the client is a persistent + client and subscription information and queued messages + will be retained when the client disconnects. + Defaults to True. + + proxy_args: a dictionary that will be given to the client. + """ + + if msg_count < 1: + raise ValueError('msg_count must be > 0') + + # Set ourselves up to return a single message if msg_count == 1, or a list + # if > 1. + if msg_count == 1: + messages = None + else: + messages = [] + + userdata = {'retained':retained, 'msg_count':msg_count, 'messages':messages} + + callback(_on_message_simple, topics, qos, userdata, hostname, port, + client_id, keepalive, will, auth, tls, protocol, transport, + clean_session, proxy_args) + + return userdata['messages'] diff --git a/Display/.venv/lib/python3.7/site-packages/paho/mqtt/subscribeoptions.py b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/subscribeoptions.py new file mode 100644 index 0000000..f55e90a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/paho/mqtt/subscribeoptions.py @@ -0,0 +1,110 @@ +""" +******************************************************************* + Copyright (c) 2017, 2019 IBM Corp. + + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + and Eclipse Distribution License v1.0 which accompany this distribution. + + The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html + and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + + Contributors: + Ian Craggs - initial implementation and/or documentation +******************************************************************* +""" + +import sys + + +class MQTTException(Exception): + pass + + +class SubscribeOptions(object): + """The MQTT v5.0 subscribe options class. + + The options are: + qos: As in MQTT v3.1.1. + noLocal: True or False. If set to True, the subscriber will not receive its own publications. + retainAsPublished: True or False. If set to True, the retain flag on received publications will be as set + by the publisher. + retainHandling: RETAIN_SEND_ON_SUBSCRIBE, RETAIN_SEND_IF_NEW_SUB or RETAIN_DO_NOT_SEND + Controls when the broker should send retained messages: + - RETAIN_SEND_ON_SUBSCRIBE: on any successful subscribe request + - RETAIN_SEND_IF_NEW_SUB: only if the subscribe request is new + - RETAIN_DO_NOT_SEND: never send retained messages + """ + + # retain handling options + RETAIN_SEND_ON_SUBSCRIBE, RETAIN_SEND_IF_NEW_SUB, RETAIN_DO_NOT_SEND = range( + 0, 3) + + def __init__(self, qos=0, noLocal=False, retainAsPublished=False, retainHandling=RETAIN_SEND_ON_SUBSCRIBE): + """ + qos: 0, 1 or 2. 0 is the default. + noLocal: True or False. False is the default and corresponds to MQTT v3.1.1 behavior. + retainAsPublished: True or False. False is the default and corresponds to MQTT v3.1.1 behavior. + retainHandling: RETAIN_SEND_ON_SUBSCRIBE, RETAIN_SEND_IF_NEW_SUB or RETAIN_DO_NOT_SEND + RETAIN_SEND_ON_SUBSCRIBE is the default and corresponds to MQTT v3.1.1 behavior. + """ + object.__setattr__(self, "names", + ["QoS", "noLocal", "retainAsPublished", "retainHandling"]) + self.QoS = qos # bits 0,1 + self.noLocal = noLocal # bit 2 + self.retainAsPublished = retainAsPublished # bit 3 + self.retainHandling = retainHandling # bits 4 and 5: 0, 1 or 2 + assert self.QoS in [0, 1, 2] + assert self.retainHandling in [ + 0, 1, 2], "Retain handling should be 0, 1 or 2" + + def __setattr__(self, name, value): + if name not in self.names: + raise MQTTException( + name + " Attribute name must be one of "+str(self.names)) + object.__setattr__(self, name, value) + + def pack(self): + assert self.QoS in [0, 1, 2] + assert self.retainHandling in [ + 0, 1, 2], "Retain handling should be 0, 1 or 2" + noLocal = 1 if self.noLocal else 0 + retainAsPublished = 1 if self.retainAsPublished else 0 + data = [(self.retainHandling << 4) | (retainAsPublished << 3) | + (noLocal << 2) | self.QoS] + if sys.version_info[0] >= 3: + buffer = bytes(data) + else: + buffer = bytearray(data) + return buffer + + def unpack(self, buffer): + b0 = buffer[0] + self.retainHandling = ((b0 >> 4) & 0x03) + self.retainAsPublished = True if ((b0 >> 3) & 0x01) == 1 else False + self.noLocal = True if ((b0 >> 2) & 0x01) == 1 else False + self.QoS = (b0 & 0x03) + assert self.retainHandling in [ + 0, 1, 2], "Retain handling should be 0, 1 or 2, not %d" % self.retainHandling + assert self.QoS in [ + 0, 1, 2], "QoS should be 0, 1 or 2, not %d" % self.QoS + return 1 + + def __repr__(self): + return str(self) + + def __str__(self): + return "{QoS="+str(self.QoS)+", noLocal="+str(self.noLocal) +\ + ", retainAsPublished="+str(self.retainAsPublished) +\ + ", retainHandling="+str(self.retainHandling)+"}" + + def json(self): + data = { + "QoS": self.QoS, + "noLocal": self.noLocal, + "retainAsPublished": self.retainAsPublished, + "retainHandling": self.retainHandling, + } + return data diff --git a/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/INSTALLER b/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/LICENSE.txt b/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/LICENSE.txt new file mode 100644 index 0000000..5cc0225 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/LICENSE.txt @@ -0,0 +1,3 @@ +This project is dual licensed under the Eclipse Public License 1.0 and the +Eclipse Distribution License 1.0 as described in the epl-v10 and edl-v10 files. + diff --git a/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/METADATA b/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/METADATA new file mode 100644 index 0000000..9a51215 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/METADATA @@ -0,0 +1,1496 @@ +Metadata-Version: 2.1 +Name: paho-mqtt +Version: 1.5.0 +Summary: MQTT version 3.1.1 client class +Home-page: http://eclipse.org/paho +Author: Roger Light +Author-email: roger@atchoo.org +License: Eclipse Public License v1.0 / Eclipse Distribution License v1.0 +Keywords: paho +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: Natural Language :: English +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Topic :: Communications +Classifier: Topic :: Internet +Provides-Extra: proxy +Requires-Dist: PySocks ; extra == 'proxy' + +Eclipse Paho™ MQTT Python Client +================================ + +This document describes the source code for the `Eclipse Paho `_ MQTT Python client library, which implements versions 3.1 and 3.1.1 of the MQTT protocol. + +This code provides a client class which enable applications to connect to an `MQTT `_ broker to publish messages, and to subscribe to topics and receive published messages. It also provides some helper functions to make publishing one off messages to an MQTT server very straightforward. + +It supports Python 2.7.9+ or 3.4+, with limited support for Python 2.7 before 2.7.9. + +The MQTT protocol is a machine-to-machine (M2M)/"Internet of Things" connectivity protocol. Designed as an extremely lightweight publish/subscribe messaging transport, it is useful for connections with remote locations where a small code footprint is required and/or network bandwidth is at a premium. + +Paho is an `Eclipse Foundation `_ project. + + +Contents +-------- + +* Installation_ +* `Known limitations`_ +* `Usage and API`_ + * `Client`_ + * `Constructor / reinitialise`_ + * `Option functions`_ + * `Connect / reconnect / disconnect`_ + * `Network loop`_ + * `Publishing`_ + * `Subscribe / Unsubscribe`_ + * `Callbacks`_ + * `External event loop support`_ + * `Global helper functions`_ + * `Publish`_ + * `Single`_ + * `Multiple`_ + * `Subscribe`_ + * `Simple`_ + * `Using Callback`_ +* `Reporting bugs`_ +* `More information`_ + + +Installation +------------ + +The latest stable version is available in the Python Package Index (PyPi) and can be installed using + +:: + + pip install paho-mqtt + +Or with ``virtualenv``: + +:: + + virtualenv paho-mqtt + source paho-mqtt/bin/activate + pip install paho-mqtt + +To obtain the full code, including examples and tests, you can clone the git repository: + +:: + + git clone https://github.com/eclipse/paho.mqtt.python + + +Once you have the code, it can be installed from your repository as well: + +:: + + cd paho.mqtt.python + python setup.py install + +To perform all test (including MQTT v5 test), you also need to clone paho.mqtt.testing in paho.mqtt.python folder:: + + git clone https://github.com/eclipse/paho.mqtt.testing.git + +Known limitations +----------------- + +The following are the known unimplemented MQTT feature. + +When clean_session is False, the session is only stored in memory not persisted. This means that +when client is restarted (not just reconnected, the object is recreated usually because the +program was restarted) the session is lost. This result in possible message lost. + +The following part of client session is lost: + +* QoS 2 messages which have been received from the Server, but have not been completely acknowledged. + + Since the client will blindly acknowledge any PUBCOMP (last message of a QoS 2 transaction), it + won't hang but will lost this QoS 2 message. + +* QoS 1 and QoS 2 messages which have been sent to the Server, but have not been completely acknowledged. + + This means that message passed to publish() may be lost. This could be mitigated by taking care + that all message passed to publish() has a corresponding on_publish() call. + + It also means that the broker may have the Qos2 message in the session. Since the client start + with an empty session it don't know it and will re-use the mid. This is not yet fixed. + +Also when clean_session is True, this library will republish QoS > 0 message accross network +reconnection. This means that QoS > 0 message won't be lost. But the standard say that +if we should discard any message for which the publish packet was sent. Our choice means that +we are not compliant with the standard and it's possible for QoS 2 to be received twice. +You should you clean_session = False if you need the QoS 2 guarantee of only one delivery. + +Usage and API +------------- + +Detailed API documentation is available through **pydoc**. Samples are available in the **examples** directory. + +The package provides two modules, a full client and a helper for simple publishing. + +Getting Started +*************** + +Here is a very simple example that subscribes to the broker $SYS topic tree and prints out the resulting messages: + +.. code:: python + + import paho.mqtt.client as mqtt + + # The callback for when the client receives a CONNACK response from the server. + def on_connect(client, userdata, flags, rc): + print("Connected with result code "+str(rc)) + + # Subscribing in on_connect() means that if we lose the connection and + # reconnect then subscriptions will be renewed. + client.subscribe("$SYS/#") + + # The callback for when a PUBLISH message is received from the server. + def on_message(client, userdata, msg): + print(msg.topic+" "+str(msg.payload)) + + client = mqtt.Client() + client.on_connect = on_connect + client.on_message = on_message + + client.connect("mqtt.eclipse.org", 1883, 60) + + # Blocking call that processes network traffic, dispatches callbacks and + # handles reconnecting. + # Other loop*() functions are available that give a threaded interface and a + # manual interface. + client.loop_forever() + +Client +****** + +You can use the client class as an instance, within a class or by subclassing. The general usage flow is as follows: + +* Create a client instance +* Connect to a broker using one of the ``connect*()`` functions +* Call one of the ``loop*()`` functions to maintain network traffic flow with the broker +* Use ``subscribe()`` to subscribe to a topic and receive messages +* Use ``publish()`` to publish messages to the broker +* Use ``disconnect()`` to disconnect from the broker + +Callbacks will be called to allow the application to process events as necessary. These callbacks are described below. + +Constructor / reinitialise +`````````````````````````` + +Client() +'''''''' + +.. code:: python + + Client(client_id="", clean_session=True, userdata=None, protocol=MQTTv311, transport="tcp") + +The ``Client()`` constructor takes the following arguments: + +client_id + the unique client id string used when connecting to the broker. If + ``client_id`` is zero length or ``None``, then one will be randomly + generated. In this case the ``clean_session`` parameter must be ``True``. + +clean_session + a boolean that determines the client type. If ``True``, the broker will + remove all information about this client when it disconnects. If ``False``, + the client is a durable client and subscription information and queued + messages will be retained when the client disconnects. + + Note that a client will never discard its own outgoing messages on + disconnect. Calling connect() or reconnect() will cause the messages to be + resent. Use reinitialise() to reset a client to its original state. + +userdata + user defined data of any type that is passed as the ``userdata`` parameter + to callbacks. It may be updated at a later point with the + ``user_data_set()`` function. + +protocol + the version of the MQTT protocol to use for this client. Can be either + ``MQTTv31`` or ``MQTTv311`` + +transport + set to "websockets" to send MQTT over WebSockets. Leave at the default of + "tcp" to use raw TCP. + + +Constructor Example +................... + +.. code:: python + + import paho.mqtt.client as mqtt + + mqttc = mqtt.Client() + + +reinitialise() +'''''''''''''' + +.. code:: python + + reinitialise(client_id="", clean_session=True, userdata=None) + +The ``reinitialise()`` function resets the client to its starting state as if it had just been created. It takes the same arguments as the ``Client()`` constructor. + +Reinitialise Example +.................... + +.. code:: python + + mqttc.reinitialise() + +Option functions +```````````````` + +These functions represent options that can be set on the client to modify its behaviour. In the majority of cases this must be done *before* connecting to a broker. + +max_inflight_messages_set() +''''''''''''''''''''''''''' + +.. code:: python + + max_inflight_messages_set(self, inflight) + +Set the maximum number of messages with QoS>0 that can be part way through their network flow at once. + +Defaults to 20. Increasing this value will consume more memory but can increase throughput. + +max_queued_messages_set() +''''''''''''''''''''''''' + +.. code:: python + + max_queued_messages_set(self, queue_size) + +Set the maximum number of outgoing messages with QoS>0 that can be pending in the outgoing message queue. + +Defaults to 0. 0 means unlimited. When the queue is full, any further outgoing messages would be dropped. + +message_retry_set() +''''''''''''''''''' + +.. code:: python + + message_retry_set(retry) + +Set the time in seconds before a message with QoS>0 is retried, if the broker does not respond. + +This is set to 5 seconds by default and should not normally need changing. + +ws_set_options() +'''''''''''''''' + +.. code:: python + + ws_set_options(self, path="/mqtt", headers=None) + +Set websocket connection options. These options will only be used if ``transport="websockets"`` was passed into the ``Client()`` constructor. + +path + The mqtt path to use on the broker. + +headers + Either a dictionary specifying a list of extra headers which should be appended to the standard websocket headers, or a callable that takes the normal websocket headers and returns a new dictionary with a set of headers to connect to the broker. + +Must be called before ``connect*()``. An example of how this can be used with the AWS IoT platform is in the **examples** folder. + + +tls_set() +''''''''' + +.. code:: python + + tls_set(ca_certs=None, certfile=None, keyfile=None, cert_reqs=ssl.CERT_REQUIRED, + tls_version=ssl.PROTOCOL_TLS, ciphers=None) + +Configure network encryption and authentication options. Enables SSL/TLS support. + +ca_certs + a string path to the Certificate Authority certificate files that are to be treated as trusted by this client. If this is the only option given then the client will operate in a similar manner to a web browser. That is to say it will require the broker to have a certificate signed by the Certificate Authorities in ``ca_certs`` and will communicate using TLS v1, but will not attempt any form of authentication. This provides basic network encryption but may not be sufficient depending on how the broker is configured. By default, on Python 2.7.9+ or 3.4+, the default certification authority of the system is used. On older Python version this parameter is mandatory. + +certfile, keyfile + strings pointing to the PEM encoded client certificate and private keys respectively. If these arguments are not ``None`` then they will be used as client information for TLS based authentication. Support for this feature is broker dependent. Note that if either of these files in encrypted and needs a password to decrypt it, Python will ask for the password at the command line. It is not currently possible to define a callback to provide the password. + +cert_reqs + defines the certificate requirements that the client imposes on the broker. By default this is ``ssl.CERT_REQUIRED``, which means that the broker must provide a certificate. See the ssl pydoc for more information on this parameter. + +tls_version + specifies the version of the SSL/TLS protocol to be used. By default (if the python version supports it) the highest TLS version is detected. If unavailable, TLS v1 is used. Previous versions (all versions beginning with SSL) are possible but not recommended due to possible security problems. + +ciphers + a string specifying which encryption ciphers are allowable for this connection, or ``None`` to use the defaults. See the ssl pydoc for more information. + +Must be called before ``connect*()``. + +tls_set_context() +''''''''''''''''' + +.. code:: python + + tls_set_context(context=None) + +Configure network encryption and authentication context. Enables SSL/TLS support. + +context + an ssl.SSLContext object. By default, this is given by ``ssl.create_default_context()``, if available (added in Python 3.4). + +If you're unsure about using this method, then either use the default context, or use the ``tls_set`` method. See the ssl module documentation section about `security considerations `_ for more information. + +Must be called before ``connect*()``. + +tls_insecure_set() +'''''''''''''''''' + +.. code:: python + + tls_insecure_set(value) + +Configure verification of the server hostname in the server certificate. + +If ``value`` is set to ``True``, it is impossible to guarantee that the host you are connecting to is not impersonating your server. This can be useful in initial server testing, but makes it possible for a malicious third party to impersonate your server through DNS spoofing, for example. + +Do not use this function in a real system. Setting value to True means there is no point using encryption. + +Must be called before ``connect*()`` and after ``tls_set()`` or ``tls_set_context()``. + +enable_logger() +''''''''''''''' + +.. code:: python + + enable_logger(logger=None) + +Enable logging using the standard python logging package (See PEP 282). This may be used at the same time as the ``on_log`` callback method. + +If ``logger`` is specified, then that ``logging.Logger`` object will be used, otherwise one will be created automatically. + +Paho logging levels are converted to standard ones according to the following mapping: + +==================== =============== +Paho logging +==================== =============== +``MQTT_LOG_ERR`` ``logging.ERROR`` +``MQTT_LOG_WARNING`` ``logging.WARNING`` +``MQTT_LOG_NOTICE`` ``logging.INFO`` *(no direct equivalent)* +``MQTT_LOG_INFO`` ``logging.INFO`` +``MQTT_LOG_DEBUG`` ``logging.DEBUG`` +==================== =============== + +disable_logger() +'''''''''''''''' + +.. code:: python + + disable_logger() + +Disable logging using standard python logging package. This has no effect on the ``on_log`` callback. + +username_pw_set() +''''''''''''''''' + +.. code:: python + + username_pw_set(username, password=None) + +Set a username and optionally a password for broker authentication. Must be called before ``connect*()``. + +user_data_set() +''''''''''''''' + +.. code:: python + + user_data_set(userdata) + +Set the private user data that will be passed to callbacks when events are generated. Use this for your own purpose to support your application. + +will_set() +'''''''''' + +.. code:: python + + will_set(topic, payload=None, qos=0, retain=False) + +Set a Will to be sent to the broker. If the client disconnects without calling +``disconnect()``, the broker will publish the message on its behalf. + +topic + the topic that the will message should be published on. + +payload + the message to send as a will. If not given, or set to ``None`` a zero + length message will be used as the will. Passing an int or float will + result in the payload being converted to a string representing that number. + If you wish to send a true int/float, use ``struct.pack()`` to create the + payload you require. + +qos + the quality of service level to use for the will. + +retain + if set to ``True``, the will message will be set as the "last known + good"/retained message for the topic. + +Raises a ``ValueError`` if ``qos`` is not 0, 1 or 2, or if ``topic`` is +``None`` or has zero string length. + +reconnect_delay_set +''''''''''''''''''' + +.. code:: python + + reconnect_delay_set(min_delay=1, max_delay=120) + +The client will automatically retry connection. Between each attempt +it will wait a number of seconds between ``min_delay`` and ``max_delay``. + +When the connection is lost, initially the reconnection attempt is delayed of +``min_delay`` seconds. It's doubled between subsequent attempt up to ``max_delay``. + +The delay is reset to ``min_delay`` when the connection complete (e.g. the CONNACK is +received, not just the TCP connection is established). + + +Connect / reconnect / disconnect +```````````````````````````````` + +connect() +''''''''' + +.. code:: python + + connect(host, port=1883, keepalive=60, bind_address="") + +The ``connect()`` function connects the client to a broker. This is a blocking +function. It takes the following arguments: + +host + the hostname or IP address of the remote broker + +port + the network port of the server host to connect to. Defaults to 1883. Note + that the default port for MQTT over SSL/TLS is 8883 so if you are using + ``tls_set()`` or ``tls_set_context()``, the port may need providing manually + +keepalive + maximum period in seconds allowed between communications with the broker. + If no other messages are being exchanged, this controls the rate at which + the client will send ping messages to the broker + +bind_address + the IP address of a local network interface to bind this client to, + assuming multiple interfaces exist + +Callback +........ + +When the client receives a CONNACK message from the broker in response to the +connect it generates an ``on_connect()`` callback. + +Connect Example +............... + +.. code:: python + + mqttc.connect("mqtt.eclipse.org") + +connect_async() +''''''''''''''' + +.. code:: python + + connect_async(host, port=1883, keepalive=60, bind_address="") + +Use in conjunction with ``loop_start()`` to connect in a non-blocking manner. +The connection will not complete until ``loop_start()`` is called. + +Callback (connect) +.................. + +When the client receives a CONNACK message from the broker in response to the +connect it generates an ``on_connect()`` callback. + +connect_srv() +''''''''''''' + +.. code:: python + + connect_srv(domain, keepalive=60, bind_address="") + +Connect to a broker using an SRV DNS lookup to obtain the broker address. Takes +the following arguments: + +domain + the DNS domain to search for SRV records. If ``None``, try to determine the + local domain name. + +See ``connect()`` for a description of the ``keepalive`` and ``bind_address`` +arguments. + +Callback (connect_srv) +...................... + +When the client receives a CONNACK message from the broker in response to the +connect it generates an ``on_connect()`` callback. + +SRV Connect Example +................... + +.. code:: python + + mqttc.connect_srv("eclipse.org") + +reconnect() +''''''''''' + +.. code:: python + + reconnect() + +Reconnect to a broker using the previously provided details. You must have +called ``connect*()`` before calling this function. + +Callback (reconnect) +.................... + +When the client receives a CONNACK message from the broker in response to the +connect it generates an ``on_connect()`` callback. + +disconnect() +'''''''''''' + +.. code:: python + + disconnect() + +Disconnect from the broker cleanly. Using ``disconnect()`` will not result in a +will message being sent by the broker. + +Disconnect will not wait for all queued message to be sent, to ensure all messages +are delivered, ``wait_for_publish()`` from ``MQTTMessageInfo`` should be used. +See ``publish()`` for details. + +Callback (disconnect) +..................... + +When the client has sent the disconnect message it generates an +``on_disconnect()`` callback. + +Network loop +```````````` + +These functions are the driving force behind the client. If they are not +called, incoming network data will not be processed and outgoing network data +may not be sent in a timely fashion. There are four options for managing the +network loop. Three are described here, the fourth in "External event loop +support" below. Do not mix the different loop functions. + +loop() +'''''' + +.. code:: python + + loop(timeout=1.0, max_packets=1) + +Call regularly to process network events. This call waits in ``select()`` until +the network socket is available for reading or writing, if appropriate, then +handles the incoming/outgoing data. This function blocks for up to ``timeout`` +seconds. ``timeout`` must not exceed the ``keepalive`` value for the client or +your client will be regularly disconnected by the broker. + +The ``max_packets`` argument is obsolete and should be left unset. + +Loop Example +............ + +.. code:: python + + run = True + while run: + mqttc.loop() + +loop_start() / loop_stop() +'''''''''''''''''''''''''' + +.. code:: python + + loop_start() + loop_stop(force=False) + +These functions implement a threaded interface to the network loop. Calling +``loop_start()`` once, before or after ``connect*()``, runs a thread in the +background to call ``loop()`` automatically. This frees up the main thread for +other work that may be blocking. This call also handles reconnecting to the +broker. Call ``loop_stop()`` to stop the background thread. The ``force`` +argument is currently ignored. + +Loop Start/Stop Example +....................... + +.. code:: python + + mqttc.connect("mqtt.eclipse.org") + mqttc.loop_start() + + while True: + temperature = sensor.blocking_read() + mqttc.publish("paho/temperature", temperature) + +loop_forever() +'''''''''''''' + +.. code:: python + + loop_forever(timeout=1.0, max_packets=1, retry_first_connection=False) + +This is a blocking form of the network loop and will not return until the +client calls ``disconnect()``. It automatically handles reconnecting. + +Except for the first connection attempt when using connect_async, use +``retry_first_connection=True`` to make it retry the first connection. +Warning: This might lead to situations where the client keeps connecting to an +non existing host without failing. + +The ``timeout`` and ``max_packets`` arguments are obsolete and should be left +unset. + +Publishing +`````````` + +Send a message from the client to the broker. + +publish() +''''''''' + +.. code:: python + + publish(topic, payload=None, qos=0, retain=False) + +This causes a message to be sent to the broker and subsequently from the broker +to any clients subscribing to matching topics. It takes the following +arguments: + +topic + the topic that the message should be published on + +payload + the actual message to send. If not given, or set to ``None`` a zero length + message will be used. Passing an int or float will result in the payload + being converted to a string representing that number. If you wish to send a + true int/float, use ``struct.pack()`` to create the payload you require + +qos + the quality of service level to use + +retain + if set to ``True``, the message will be set as the "last known + good"/retained message for the topic. + +Returns a MQTTMessageInfo which expose the following attributes and methods: + +* ``rc``, the result of the publishing. It could be ``MQTT_ERR_SUCCESS`` to + indicate success, ``MQTT_ERR_NO_CONN`` if the client is not currently connected, + or ``MQTT_ERR_QUEUE_SIZE`` when ``max_queued_messages_set`` is used to indicate + that message is neither queued nor sent. +* ``mid`` is the message ID for the publish request. The mid value can be used to + track the publish request by checking against the mid argument in the + ``on_publish()`` callback if it is defined. ``wait_for_publish`` may be easier + depending on your use-case. +* ``wait_for_publish()`` will block until the message is published. It will + raise ValueError if the message is not queued (rc == ``MQTT_ERR_QUEUE_SIZE``). +* ``is_published`` returns True if the message has been published. It will + raise ValueError if the message is not queued (rc == ``MQTT_ERR_QUEUE_SIZE``). + +A ``ValueError`` will be raised if topic is ``None``, has zero length or is +invalid (contains a wildcard), if ``qos`` is not one of 0, 1 or 2, or if the +length of the payload is greater than 268435455 bytes. + +Callback (publish) +.................. + +When the message has been sent to the broker an ``on_publish()`` callback will +be generated. + + +Subscribe / Unsubscribe +``````````````````````` + +subscribe() +''''''''''' + +.. code:: python + + subscribe(topic, qos=0) + +Subscribe the client to one or more topics. + +This function may be called in three different ways: + +Simple string and integer +......................... + +e.g. ``subscribe("my/topic", 2)`` + +topic + a string specifying the subscription topic to subscribe to. + +qos + the desired quality of service level for the subscription. Defaults to 0. + +String and integer tuple +........................ + +e.g. ``subscribe(("my/topic", 1))`` + +topic + a tuple of ``(topic, qos)``. Both topic and qos must be present in the tuple. + +qos + not used. + +List of string and integer tuples +................................. + +e.g. ``subscribe([("my/topic", 0), ("another/topic", 2)])`` + +This allows multiple topic subscriptions in a single SUBSCRIPTION command, +which is more efficient than using multiple calls to ``subscribe()``. + +topic + a list of tuple of format ``(topic, qos)``. Both topic and qos must be + present in all of the tuples. + +qos + not used. + +The function returns a tuple ``(result, mid)``, where ``result`` is +``MQTT_ERR_SUCCESS`` to indicate success or ``(MQTT_ERR_NO_CONN, None)`` if the +client is not currently connected. ``mid`` is the message ID for the subscribe +request. The mid value can be used to track the subscribe request by checking +against the mid argument in the ``on_subscribe()`` callback if it is defined. + +Raises a ``ValueError`` if ``qos`` is not 0, 1 or 2, or if topic is ``None`` or +has zero string length, or if ``topic`` is not a string, tuple or list. + +Callback (subscribe) +.................... + +When the broker has acknowledged the subscription, an ``on_subscribe()`` +callback will be generated. + +unsubscribe() +''''''''''''' + +.. code:: python + + unsubscribe(topic) + +Unsubscribe the client from one or more topics. + +topic + a single string, or list of strings that are the subscription topics to + unsubscribe from. + +Returns a tuple ``(result, mid)``, where ``result`` is ``MQTT_ERR_SUCCESS`` to +indicate success, or ``(MQTT_ERR_NO_CONN, None)`` if the client is not +currently connected. ``mid`` is the message ID for the unsubscribe request. The +mid value can be used to track the unsubscribe request by checking against the +mid argument in the ``on_unsubscribe()`` callback if it is defined. + +Raises a ``ValueError`` if ``topic`` is ``None`` or has zero string length, or +is not a string or list. + +Callback (unsubscribe) +...................... + +When the broker has acknowledged the unsubscribe, an ``on_unsubscribe()`` +callback will be generated. + +Callbacks +````````` + +on_connect() +'''''''''''' + +.. code:: python + + on_connect(client, userdata, flags, rc) + +Called when the broker responds to our connection request. + +client + the client instance for this callback + +userdata + the private user data as set in ``Client()`` or ``user_data_set()`` + +flags + response flags sent by the broker +rc + the connection result + + +flags is a dict that contains response flags from the broker: + flags['session present'] - this flag is useful for clients that are + using clean session set to 0 only. If a client with clean + session=0, that reconnects to a broker that it has previously + connected to, this flag indicates whether the broker still has the + session information for the client. If 1, the session still exists. + +The value of rc indicates success or not: + + 0: Connection successful + 1: Connection refused - incorrect protocol version + 2: Connection refused - invalid client identifier + 3: Connection refused - server unavailable + 4: Connection refused - bad username or password + 5: Connection refused - not authorised + 6-255: Currently unused. + +On Connect Example +.................. + +.. code:: python + + def on_connect(client, userdata, flags, rc): + print("Connection returned result: "+connack_string(rc)) + + mqttc.on_connect = on_connect + ... + +on_disconnect() +''''''''''''''' + +.. code:: python + + on_disconnect(client, userdata, rc) + +Called when the client disconnects from the broker. + +client + the client instance for this callback + +userdata + the private user data as set in ``Client()`` or ``user_data_set()`` + +rc + the disconnection result + +The rc parameter indicates the disconnection state. If ``MQTT_ERR_SUCCESS`` +(0), the callback was called in response to a ``disconnect()`` call. If any +other value the disconnection was unexpected, such as might be caused by a +network error. + +On Disconnect Example +..................... + +.. code:: python + + def on_disconnect(client, userdata, rc): + if rc != 0: + print("Unexpected disconnection.") + + mqttc.on_disconnect = on_disconnect + ... + +on_message() +'''''''''''' + +.. code:: python + + on_message(client, userdata, message) + +Called when a message has been received on a topic that the client subscribes +to and the message does not match an existing topic filter callback. +Use ``message_callback_add()`` to define a callback that will be called for +specific topic filters. ``on_message`` will serve as fallback when none matched. + +client + the client instance for this callback + +userdata + the private user data as set in ``Client()`` or ``user_data_set()`` + +message + an instance of MQTTMessage. This is a class with members ``topic``, ``payload``, ``qos``, ``retain``. + +On Message Example +.................. + +.. code:: python + + def on_message(client, userdata, message): + print("Received message '" + str(message.payload) + "' on topic '" + + message.topic + "' with QoS " + str(message.qos)) + + mqttc.on_message = on_message + ... + +message_callback_add() +'''''''''''''''''''''' + +This function allows you to define callbacks that handle incoming messages for +specific subscription filters, including with wildcards. This lets you, for +example, subscribe to ``sensors/#`` and have one callback to handle +``sensors/temperature`` and another to handle ``sensors/humidity``. + +.. code:: python + + message_callback_add(sub, callback) + +sub + the subscription filter to match against for this callback. Only one + callback may be defined per literal sub string + +callback + the callback to be used. Takes the same form as the ``on_message`` + callback. + +If using ``message_callback_add()`` and ``on_message``, only messages that do +not match a subscription specific filter will be passed to the ``on_message`` +callback. + +If multiple sub match a topic, each callback will be called (e.g. sub ``sensors/#`` +and sub ``+/humidity`` both match a message with a topic ``sensors/humidity``, so both +callbacks will handle this message). + +message_callback_remove() +''''''''''''''''''''''''' + +Remove a topic/subscription specific callback previously registered using +``message_callback_add()``. + +.. code:: python + + message_callback_remove(sub) + +sub + the subscription filter to remove + +on_publish() +'''''''''''' + +.. code:: python + + on_publish(client, userdata, mid) + +Called when a message that was to be sent using the ``publish()`` call has +completed transmission to the broker. For messages with QoS levels 1 and 2, +this means that the appropriate handshakes have completed. For QoS 0, this +simply means that the message has left the client. The ``mid`` variable matches +the mid variable returned from the corresponding ``publish()`` call, to allow +outgoing messages to be tracked. + +This callback is important because even if the publish() call returns success, +it does not always mean that the message has been sent. + +on_subscribe() +'''''''''''''' + +.. code:: python + + on_subscribe(client, userdata, mid, granted_qos) + +Called when the broker responds to a subscribe request. The ``mid`` variable +matches the mid variable returned from the corresponding ``subscribe()`` call. +The ``granted_qos`` variable is a list of integers that give the QoS level the +broker has granted for each of the different subscription requests. + +on_unsubscribe() +'''''''''''''''' + +.. code:: python + + on_unsubscribe(client, userdata, mid) + +Called when the broker responds to an unsubscribe request. The ``mid`` variable +matches the mid variable returned from the corresponding ``unsubscribe()`` +call. + +on_log() +'''''''' + +.. code:: python + + on_log(client, userdata, level, buf) + +Called when the client has log information. Define to allow debugging. The +``level`` variable gives the severity of the message and will be one of +``MQTT_LOG_INFO``, ``MQTT_LOG_NOTICE``, ``MQTT_LOG_WARNING``, ``MQTT_LOG_ERR``, +and ``MQTT_LOG_DEBUG``. The message itself is in ``buf``. + +This may be used at the same time as the standard Python logging, which can be +enabled via the ``enable_logger`` method. + +on_socket_open() +'''''''''''''''' + +:: + + on_socket_open(client, userdata, sock) + +Called when the socket has been opened. +Use this to register the socket with an external event loop for reading. + +on_socket_close() +''''''''''''''''' + +:: + + on_socket_close(client, userdata, sock) + +Called when the socket is about to be closed. +Use this to unregister a socket from an external event loop for reading. + +on_socket_register_write() +'''''''''''''''''''''''''' + +:: + + on_socket_register_write(client, userdata, sock) + +Called when a write operation to the socket failed because it would have blocked, e.g. output buffer full. +Use this to register the socket with an external event loop for writing. + +on_socket_unregister_write() +'''''''''''''''''''''''''''' + +:: + + on_socket_unregister_write(client, userdata, sock) + +Called when a write operation to the socket succeeded after it had previously failed. +Use this to unregister the socket from an external event loop for writing. + +External event loop support +``````````````````````````` + +loop_read() +''''''''''' + +.. code:: python + + loop_read(max_packets=1) + +Call when the socket is ready for reading. ``max_packets`` is obsolete and +should be left unset. + +loop_write() +'''''''''''' + +.. code:: python + + loop_write(max_packets=1) + +Call when the socket is ready for writing. ``max_packets`` is obsolete and +should be left unset. + +loop_misc() +''''''''''' + +.. code:: python + + loop_misc() + +Call every few seconds to handle message retrying and pings. + +socket() +'''''''' + +.. code:: python + + socket() + +Returns the socket object in use in the client to allow interfacing with other +event loops. +This call is particularly useful for select_ based loops. See ``examples/loop_select.py``. + +.. _select: https://docs.python.org/3/library/select.html#select.select + +want_write() +'''''''''''' + +.. code:: python + + want_write() + +Returns true if there is data waiting to be written, to allow interfacing the +client with other event loops. +This call is particularly useful for select_ based loops. See ``examples/loop_select.py``. + +.. _select: https://docs.python.org/3/library/select.html#select.select + +state callbacks +''''''''''''''' + +:: + + on_socket_open + on_socket_close + on_socket_register_write + on_socket_unregister_write + +Use these callbacks to get notified about state changes in the socket. +This is particularly useful for event loops where you register or unregister a socket +for reading+writing. See ``examples/loop_asyncio.py`` for an example. + +When the socket is opened, ``on_socket_open`` is called. +Register the socket with your event loop for reading. + +When the socket is about to be closed, ``on_socket_close`` is called. +Unregister the socket from your event loop for reading. + +When a write to the socket failed because it would have blocked, e.g. output buffer full, +``on_socket_register_write`` is called. +Register the socket with your event loop for writing. + +When the next write to the socket succeeded, ``on_socket_unregister_write`` is called. +Unregister the socket from your event loop for writing. + +The callbacks are always called in this order: + +- ``on_socket_open`` +- Zero or more times: + + - ``on_socket_register_write`` + - ``on_socket_unregister_write`` + +- ``on_socket_close`` + +Global helper functions +``````````````````````` + +The client module also offers some global helper functions. + +``topic_matches_sub(sub, topic)`` can be used to check whether a ``topic`` +matches a ``subscription``. + +For example: + + the topic ``foo/bar`` would match the subscription ``foo/#`` or ``+/bar`` + + the topic ``non/matching`` would not match the subscription ``non/+/+`` + + +``connack_string(connack_code)`` returns the error string associated with a +CONNACK result. + + +``error_string(mqtt_errno)`` returns the error string associated with a Paho +MQTT error number. + +Publish +******* + +This module provides some helper functions to allow straightforward publishing +of messages in a one-shot manner. In other words, they are useful for the +situation where you have a single/multiple messages you want to publish to a +broker, then disconnect with nothing else required. + +The two functions provided are ``single()`` and ``multiple()``. + +Single +`````` + +Publish a single message to a broker, then disconnect cleanly. + +.. code:: python + + single(topic, payload=None, qos=0, retain=False, hostname="localhost", + port=1883, client_id="", keepalive=60, will=None, auth=None, tls=None, + protocol=mqtt.MQTTv311, transport="tcp") + + +Publish Single Function arguments +''''''''''''''''''''''''''''''''' + +topic + the only required argument must be the topic string to which the payload + will be published. + +payload + the payload to be published. If "" or None, a zero length payload will be + published. + +qos + the qos to use when publishing, default to 0. + +retain + set the message to be retained (True) or not (False). + +hostname + a string containing the address of the broker to connect to. Defaults to + localhost. + +port + the port to connect to the broker on. Defaults to 1883. + +client_id + the MQTT client id to use. If "" or None, the Paho library will + generate a client id automatically. + +keepalive + the keepalive timeout value for the client. Defaults to 60 seconds. + +will + a dict containing will parameters for the client: + + will = {'topic': "", 'payload':", 'qos':, 'retain':}. + + Topic is required, all other parameters are optional and will default to + None, 0 and False respectively. + + Defaults to None, which indicates no will should be used. + +auth + a dict containing authentication parameters for the client: + + auth = {'username':"", 'password':""} + + Username is required, password is optional and will default to None if not provided. + + Defaults to None, which indicates no authentication is to be used. + +tls + a dict containing TLS configuration parameters for the client: + + dict = {'ca_certs':"", 'certfile':"", 'keyfile':"", 'tls_version':"", 'ciphers':"} + + ca_certs is required, all other parameters are optional and will default to None if not provided, which results in the client using the default behaviour - see the paho.mqtt.client documentation. + + Defaults to None, which indicates that TLS should not be used. + +protocol + choose the version of the MQTT protocol to use. Use either ``MQTTv31`` or ``MQTTv311``. + +transport + set to "websockets" to send MQTT over WebSockets. Leave at the default of + "tcp" to use raw TCP. + +Publish Single Example +'''''''''''''''''''''' + +.. code:: python + + import paho.mqtt.publish as publish + + publish.single("paho/test/single", "payload", hostname="mqtt.eclipse.org") + +Multiple +```````` + +Publish multiple messages to a broker, then disconnect cleanly. + +.. code:: python + + multiple(msgs, hostname="localhost", port=1883, client_id="", keepalive=60, + will=None, auth=None, tls=None, protocol=mqtt.MQTTv311, transport="tcp") + +Publish Multiple Function arguments +''''''''''''''''''''''''''''''''''' + +msgs + a list of messages to publish. Each message is either a dict or a tuple. + + If a dict, only the topic must be present. Default values will be + used for any missing arguments. The dict must be of the form: + + msg = {'topic':"", 'payload':"", 'qos':, 'retain':} + + topic must be present and may not be empty. + If payload is "", None or not present then a zero length payload will be published. If qos is not present, the default of 0 is used. If retain is not present, the default of False is used. + + If a tuple, then it must be of the form: + + ("", "", qos, retain) + +See ``single()`` for the description of ``hostname``, ``port``, ``client_id``, ``keepalive``, ``will``, ``auth``, ``tls``, ``protocol``, ``transport``. + +Publish Multiple Example +'''''''''''''''''''''''' + +.. code:: python + + import paho.mqtt.publish as publish + + msgs = [{'topic':"paho/test/multiple", 'payload':"multiple 1"}, + ("paho/test/multiple", "multiple 2", 0, False)] + publish.multiple(msgs, hostname="mqtt.eclipse.org") + + +Subscribe +********* + +This module provides some helper functions to allow straightforward subscribing +and processing of messages. + +The two functions provided are ``simple()`` and ``callback()``. + +Simple +`````` + +Subscribe to a set of topics and return the messages received. This is a +blocking function. + +.. code:: python + + simple(topics, qos=0, msg_count=1, retained=False, hostname="localhost", + port=1883, client_id="", keepalive=60, will=None, auth=None, tls=None, + protocol=mqtt.MQTTv311) + + +Simple Subscribe Function arguments +''''''''''''''''''''''''''''''''''' + +topics + the only required argument is the topic string to which the client will + subscribe. This can either be a string or a list of strings if multiple + topics should be subscribed to. + +qos + the qos to use when subscribing, defaults to 0. + +msg_count + the number of messages to retrieve from the broker. Defaults to 1. If 1, a + single MQTTMessage object will be returned. If >1, a list of MQTTMessages + will be returned. + +retained + set to True to consider retained messages, set to False to ignore messages + with the retained flag set. + +hostname + a string containing the address of the broker to connect to. Defaults to localhost. + +port + the port to connect to the broker on. Defaults to 1883. + +client_id + the MQTT client id to use. If "" or None, the Paho library will + generate a client id automatically. + +keepalive + the keepalive timeout value for the client. Defaults to 60 seconds. + +will + a dict containing will parameters for the client: + + will = {'topic': "", 'payload':", 'qos':, 'retain':}. + + Topic is required, all other parameters are optional and will default to + None, 0 and False respectively. + + Defaults to None, which indicates no will should be used. + +auth + a dict containing authentication parameters for the client: + + auth = {'username':"", 'password':""} + + Username is required, password is optional and will default to None if not + provided. + + Defaults to None, which indicates no authentication is to be used. + +tls + a dict containing TLS configuration parameters for the client: + + dict = {'ca_certs':"", 'certfile':"", 'keyfile':"", 'tls_version':"", 'ciphers':"} + + ca_certs is required, all other parameters are optional and will default to + None if not provided, which results in the client using the default + behaviour - see the paho.mqtt.client documentation. + + Defaults to None, which indicates that TLS should not be used. + +protocol + choose the version of the MQTT protocol to use. Use either ``MQTTv31`` or ``MQTTv311``. + + +Simple Example +'''''''''''''' + +.. code:: python + + import paho.mqtt.subscribe as subscribe + + msg = subscribe.simple("paho/test/simple", hostname="mqtt.eclipse.org") + print("%s %s" % (msg.topic, msg.payload)) + +Using Callback +`````````````` + +Subscribe to a set of topics and process the messages received using a user +provided callback. + +.. code:: python + + callback(callback, topics, qos=0, userdata=None, hostname="localhost", + port=1883, client_id="", keepalive=60, will=None, auth=None, tls=None, + protocol=mqtt.MQTTv311) + +Callback Subscribe Function arguments +''''''''''''''''''''''''''''''''''''' + +callback + an "on_message" callback that will be used for each message received, and + of the form + + .. code:: python + + def on_message(client, userdata, message) + +topics + the topic string to which the client will subscribe. This can either be a + string or a list of strings if multiple topics should be subscribed to. + +qos + the qos to use when subscribing, defaults to 0. + +userdata + a user provided object that will be passed to the on_message callback when + a message is received. + +See ``simple()`` for the description of ``hostname``, ``port``, ``client_id``, ``keepalive``, ``will``, ``auth``, ``tls``, ``protocol``. + +Callback Example +'''''''''''''''' + +.. code:: python + + import paho.mqtt.subscribe as subscribe + + def on_message_print(client, userdata, message): + print("%s %s" % (message.topic, message.payload)) + + subscribe.callback(on_message_print, "paho/test/callback", hostname="mqtt.eclipse.org") + + +Reporting bugs +-------------- + +Please report bugs in the issues tracker at https://github.com/eclipse/paho.mqtt.python/issues. + +More information +---------------- + +Discussion of the Paho clients takes place on the `Eclipse paho-dev mailing list `_. + +General questions about the MQTT protocol itself (not this library) are discussed in the `MQTT Google Group `_. + +There is much more information available via the `MQTT community site `_. + + diff --git a/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/RECORD b/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/RECORD new file mode 100644 index 0000000..c7f77a9 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/RECORD @@ -0,0 +1,26 @@ +paho/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +paho/__pycache__/__init__.cpython-37.pyc,, +paho/mqtt/__init__.py,sha256=sfk8-UKXWLNHdycgRjeHLBpj6MRcFcseF-niQzO9VvU,65 +paho/mqtt/__pycache__/__init__.cpython-37.pyc,, +paho/mqtt/__pycache__/client.cpython-37.pyc,, +paho/mqtt/__pycache__/matcher.cpython-37.pyc,, +paho/mqtt/__pycache__/packettypes.cpython-37.pyc,, +paho/mqtt/__pycache__/properties.cpython-37.pyc,, +paho/mqtt/__pycache__/publish.cpython-37.pyc,, +paho/mqtt/__pycache__/reasoncodes.cpython-37.pyc,, +paho/mqtt/__pycache__/subscribe.cpython-37.pyc,, +paho/mqtt/__pycache__/subscribeoptions.cpython-37.pyc,, +paho/mqtt/client.py,sha256=BUoLCYMt9e3T1vOTfTaRWXfdcer3iYK7My2oy0v5YMk,150847 +paho/mqtt/matcher.py,sha256=decaa8yM4QsVgGHZL9jPehCHzLtFZhxzzHkelqHr3DQ,2775 +paho/mqtt/packettypes.py,sha256=D-ZARRzaozOkZHyZ5WkeU8P6VoEREjJNClA8-_MwJ0w,1453 +paho/mqtt/properties.py,sha256=Odi-vU-aWQMoiBUEkTMRoIrKyKdlpG4Uy-tCSfaTi_A,16499 +paho/mqtt/publish.py,sha256=Au5GHWdZ9iDYogi4tzAJApGCE6gLyCz1VP2LhSKLdKw,9297 +paho/mqtt/reasoncodes.py,sha256=OjAOuf31_G71l3ai9m2XTaSmvjnTZCojrNwsFPkt9Mc,8590 +paho/mqtt/subscribe.py,sha256=-dkK-vutYCxeKllTF1MPPXlfQFSJZERYFnr4yBYaPAA,11138 +paho/mqtt/subscribeoptions.py,sha256=uOuGABVFP2MVm-NjAPTaxBXiDfoLwxvjfpcB9hjqeVE,4616 +paho_mqtt-1.5.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +paho_mqtt-1.5.0.dist-info/LICENSE.txt,sha256=dvE3KehOkiLlQzA98A-H0bLAmVtqUFzYWaKFZn5Eurs,156 +paho_mqtt-1.5.0.dist-info/METADATA,sha256=ndnSuaO2UwTFwD9x0GrhOS6voPgNBV416DtRauqfrgA,46542 +paho_mqtt-1.5.0.dist-info/RECORD,, +paho_mqtt-1.5.0.dist-info/WHEEL,sha256=GBf6745TJ8PUSVf00LTQs_w-OsUzD9Wc795tFJt0GUs,93 +paho_mqtt-1.5.0.dist-info/top_level.txt,sha256=Yz3Ft1eA2xY97jYx6JarAsWfYVa545FiTBANZfnKQSM,5 diff --git a/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/WHEEL b/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/WHEEL new file mode 100644 index 0000000..cb6dbf4 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.32.3) +Root-Is-Purelib: true +Tag: cp37-none-any + diff --git a/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/top_level.txt b/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/top_level.txt new file mode 100644 index 0000000..97b8766 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/paho_mqtt-1.5.0.dist-info/top_level.txt @@ -0,0 +1 @@ +paho diff --git a/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/INSTALLER b/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/LICENSE.txt b/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/LICENSE.txt new file mode 100644 index 0000000..d3379fa --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2008-2018 The pip developers (see AUTHORS.txt file) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/METADATA b/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/METADATA new file mode 100644 index 0000000..2e314aa --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/METADATA @@ -0,0 +1,70 @@ +Metadata-Version: 2.1 +Name: pip +Version: 18.1 +Summary: The PyPA recommended tool for installing Python packages. +Home-page: https://pip.pypa.io/ +Author: The pip developers +Author-email: pypa-dev@groups.google.com +License: MIT +Keywords: distutils easy_install egg setuptools wheel virtualenv +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: Software Development :: Build Tools +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* + +pip +=== + +The `PyPA recommended`_ tool for installing Python packages. + +.. image:: https://img.shields.io/pypi/v/pip.svg + :target: https://pypi.org/project/pip/ + +.. image:: https://img.shields.io/travis/pypa/pip/master.svg?label=travis-ci + :target: https://travis-ci.org/pypa/pip + +.. image:: https://img.shields.io/appveyor/ci/pypa/pip.svg?label=appveyor-ci + :target: https://ci.appveyor.com/project/pypa/pip/history + +.. image:: https://readthedocs.org/projects/pip/badge/?version=latest + :target: https://pip.pypa.io/en/latest + +* `Installation`_ +* `Documentation`_ +* `Changelog`_ +* `GitHub Page`_ +* `Issue Tracking`_ +* `User mailing list`_ +* `Dev mailing list`_ +* User IRC: #pypa on Freenode. +* Dev IRC: #pypa-dev on Freenode. + +Code of Conduct +--------------- + +Everyone interacting in the pip project's codebases, issue trackers, chat +rooms and mailing lists is expected to follow the `PyPA Code of Conduct`_. + +.. _PyPA recommended: https://packaging.python.org/en/latest/current/ +.. _Installation: https://pip.pypa.io/en/stable/installing.html +.. _Documentation: https://pip.pypa.io/en/stable/ +.. _Changelog: https://pip.pypa.io/en/stable/news.html +.. _GitHub Page: https://github.com/pypa/pip +.. _Issue Tracking: https://github.com/pypa/pip/issues +.. _User mailing list: https://groups.google.com/forum/#!forum/python-virtualenv +.. _Dev mailing list: https://groups.google.com/forum/#!forum/pypa-dev +.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/ + + diff --git a/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/RECORD b/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/RECORD new file mode 100644 index 0000000..10def57 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/RECORD @@ -0,0 +1,172 @@ +../../../bin/pip,sha256=S4sBAiYMmM-04emf5YNZnHo-VzPtjqoyr8FJCElm5jo,273 +../../../bin/pip3,sha256=S4sBAiYMmM-04emf5YNZnHo-VzPtjqoyr8FJCElm5jo,273 +../../../bin/pip3.7,sha256=S4sBAiYMmM-04emf5YNZnHo-VzPtjqoyr8FJCElm5jo,273 +pip-18.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pip-18.1.dist-info/LICENSE.txt,sha256=ORqHhOMZ2uVDFHfUzJvFBPxdcf2eieHIDxzThV9dfPo,1090 +pip-18.1.dist-info/METADATA,sha256=D7pqBJTuqM9w_HTW91a0XGjLT9vynlBAE4pPCt_W_UE,2588 +pip-18.1.dist-info/RECORD,, +pip-18.1.dist-info/WHEEL,sha256=_wJFdOYk7i3xxT8ElOkUJvOdOvfNGbR9g-bf6UQT6sU,110 +pip-18.1.dist-info/entry_points.txt,sha256=S_zfxY25QtQDVY1BiLAmOKSkkI5llzCKPLiYOSEupsY,98 +pip-18.1.dist-info/top_level.txt,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pip/__init__.py,sha256=nO-iphoXiDoci_ZAMl-PG2zdd4Y7m88jBDILTYzwGy4,21 +pip/__main__.py,sha256=L3IHqBeasELUHvwy5CT_izVEMhM12tve289qut49DvU,623 +pip/__pycache__/__init__.cpython-37.pyc,, +pip/__pycache__/__main__.cpython-37.pyc,, +pip/_internal/__init__.py,sha256=b0jSFCCViGhB1RWni35_NMkH3Y-mbZrV648DGMagDjs,2869 +pip/_internal/__pycache__/__init__.cpython-37.pyc,, +pip/_internal/__pycache__/build_env.cpython-37.pyc,, +pip/_internal/__pycache__/cache.cpython-37.pyc,, +pip/_internal/__pycache__/configuration.cpython-37.pyc,, +pip/_internal/__pycache__/download.cpython-37.pyc,, +pip/_internal/__pycache__/exceptions.cpython-37.pyc,, +pip/_internal/__pycache__/index.cpython-37.pyc,, +pip/_internal/__pycache__/locations.cpython-37.pyc,, +pip/_internal/__pycache__/pep425tags.cpython-37.pyc,, +pip/_internal/__pycache__/pyproject.cpython-37.pyc,, +pip/_internal/__pycache__/resolve.cpython-37.pyc,, +pip/_internal/__pycache__/wheel.cpython-37.pyc,, +pip/_internal/build_env.py,sha256=zKhqmDMnrX5OTSNQ4xBw-mN5mTGVu6wjiNFW-ajWYEI,4797 +pip/_internal/cache.py,sha256=96_aKtDbwgLEVNgNabOT8GrFCYZEACedoiucqU5ccg8,6829 +pip/_internal/cli/__init__.py,sha256=FkHBgpxxb-_gd6r1FjnNhfMOzAUYyXoXKJ6abijfcFU,132 +pip/_internal/cli/__pycache__/__init__.cpython-37.pyc,, +pip/_internal/cli/__pycache__/autocompletion.cpython-37.pyc,, +pip/_internal/cli/__pycache__/base_command.cpython-37.pyc,, +pip/_internal/cli/__pycache__/cmdoptions.cpython-37.pyc,, +pip/_internal/cli/__pycache__/main_parser.cpython-37.pyc,, +pip/_internal/cli/__pycache__/parser.cpython-37.pyc,, +pip/_internal/cli/__pycache__/status_codes.cpython-37.pyc,, +pip/_internal/cli/autocompletion.py,sha256=ptvsMdGjq42pzoY4skABVF43u2xAtLJlXAulPi-A10Y,6083 +pip/_internal/cli/base_command.py,sha256=ke6af4iWzrZoc3HtiPKnCZJvD6GlX8dRwBwpFCg1axc,9963 +pip/_internal/cli/cmdoptions.py,sha256=klAO3AxS0_xoZY_3LwwRjT4TbxtdIwBrmnLJvgG6sGI,19467 +pip/_internal/cli/main_parser.py,sha256=Ga_kT7if-Gg0rmmRqlGEHW6JWVm9zwzO7igJm6RE9EI,2763 +pip/_internal/cli/parser.py,sha256=VZKUKJPbU6I2cHPLDOikin-aCx7OvLcZ3fzYp3xytd8,9378 +pip/_internal/cli/status_codes.py,sha256=F6uDG6Gj7RNKQJUDnd87QKqI16Us-t-B0wPF_4QMpWc,156 +pip/_internal/commands/__init__.py,sha256=CQAzhVx9ViPtqLNUvAeqnKj5iWfFEcqMx5RlZWjJ30c,2251 +pip/_internal/commands/__pycache__/__init__.cpython-37.pyc,, +pip/_internal/commands/__pycache__/check.cpython-37.pyc,, +pip/_internal/commands/__pycache__/completion.cpython-37.pyc,, +pip/_internal/commands/__pycache__/configuration.cpython-37.pyc,, +pip/_internal/commands/__pycache__/download.cpython-37.pyc,, +pip/_internal/commands/__pycache__/freeze.cpython-37.pyc,, +pip/_internal/commands/__pycache__/hash.cpython-37.pyc,, +pip/_internal/commands/__pycache__/help.cpython-37.pyc,, +pip/_internal/commands/__pycache__/install.cpython-37.pyc,, +pip/_internal/commands/__pycache__/list.cpython-37.pyc,, +pip/_internal/commands/__pycache__/search.cpython-37.pyc,, +pip/_internal/commands/__pycache__/show.cpython-37.pyc,, +pip/_internal/commands/__pycache__/uninstall.cpython-37.pyc,, +pip/_internal/commands/__pycache__/wheel.cpython-37.pyc,, +pip/_internal/commands/check.py,sha256=CyeYH2kfDKSGSURoBfWtx-sTcZZQP-bK170NmKYlmsg,1398 +pip/_internal/commands/completion.py,sha256=hqvCvoxsIHjysiD7olHKTqK2lzE1_lS6LWn69kN5qyI,2929 +pip/_internal/commands/configuration.py,sha256=265HWuUxPggCNcIeWHA3p-LDDiRVnexwFgwmHGgWOHY,7125 +pip/_internal/commands/download.py,sha256=D_iGMp3xX2iD7KZYZAjXlYT3rf3xjwxyYe05KE-DVzE,6514 +pip/_internal/commands/freeze.py,sha256=VvS3G0wrm_9BH3B7Ex5msLL_1UQTtCq5G8dDI63Iemo,3259 +pip/_internal/commands/hash.py,sha256=K1JycsD-rpjqrRcL_ijacY9UKmI82pQcLYq4kCM4Pv0,1681 +pip/_internal/commands/help.py,sha256=MwBhPJpW1Dt3GfJV3V8V6kgAy_pXT0jGrZJB1wCTW-E,1090 +pip/_internal/commands/install.py,sha256=I_zZhkmIbDm_HqLI2WWC9vjXEnd5kNAdQ2k1xtU38zg,21874 +pip/_internal/commands/list.py,sha256=n740MsR0cG34EuvGWMzdVl0uIA3UIYx1_95FUsTktN0,10272 +pip/_internal/commands/search.py,sha256=sLZ9icKMEEGekHvzRRZMiTd1zCFIZeDptyyU1mQCYzk,4728 +pip/_internal/commands/show.py,sha256=9EVh86vY0NZdlhT-wsuV-zq_MAV6qqV4S1Akn3wkUuw,6289 +pip/_internal/commands/uninstall.py,sha256=h0gfPF5jylDESx_IHgF6bZME7QAEOHzQHdn65GP-jrE,2963 +pip/_internal/commands/wheel.py,sha256=ZuVf_DMpKCUzBVstolvQPAeajQRC51Oky5_hDHzhhFs,7020 +pip/_internal/configuration.py,sha256=KMgG3ufFrUKX_QESi2cMVvFi47tl845Bg1ZkNthlWik,13243 +pip/_internal/download.py,sha256=c5Hkimq39eJdZ6DN0_0etjK43-0a5CK_W_3sVLqH87g,33300 +pip/_internal/exceptions.py,sha256=EIGotnq6qM2nbGtnlgZ8Xp5VfP2W4-9UOCzQGMwy5MY,8899 +pip/_internal/index.py,sha256=FUA7IIhNcw2wmN1TQli-DNQzZJRGqKwcwKEcW8K1KSk,34791 +pip/_internal/locations.py,sha256=ujNrLnA04Y_EmSriO0nS6qkkw_BkPfobB_hdwIDPvpM,6307 +pip/_internal/models/__init__.py,sha256=3DHUd_qxpPozfzouoqa9g9ts1Czr5qaHfFxbnxriepM,63 +pip/_internal/models/__pycache__/__init__.cpython-37.pyc,, +pip/_internal/models/__pycache__/candidate.cpython-37.pyc,, +pip/_internal/models/__pycache__/format_control.cpython-37.pyc,, +pip/_internal/models/__pycache__/index.cpython-37.pyc,, +pip/_internal/models/__pycache__/link.cpython-37.pyc,, +pip/_internal/models/candidate.py,sha256=zq2Vb5l5JflrVX7smHTJHQciZWHyoJZuYTLeQa1G16c,741 +pip/_internal/models/format_control.py,sha256=aDbH4D2XuyaGjtRjTLQhNzClAcLZdJCKSHO8xbZSmFA,2202 +pip/_internal/models/index.py,sha256=YI1WlhWfS9mVPY0bIboA5la2pjJ2J0qgPJIbvdEjZBk,996 +pip/_internal/models/link.py,sha256=E61PvS2Wrmb9-zT-eAc_8_xI3C-89wJlpL8SL-mlQmg,3998 +pip/_internal/operations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/operations/__pycache__/__init__.cpython-37.pyc,, +pip/_internal/operations/__pycache__/check.cpython-37.pyc,, +pip/_internal/operations/__pycache__/freeze.cpython-37.pyc,, +pip/_internal/operations/__pycache__/prepare.cpython-37.pyc,, +pip/_internal/operations/check.py,sha256=ahcOg5p68nNow6_wy5prYYK0KZq22lm0CsJn8AyDMCI,4937 +pip/_internal/operations/freeze.py,sha256=lskaBcqf3bPZupG032fuLf76QYv5wpAQ6jsiXac56Bg,10450 +pip/_internal/operations/prepare.py,sha256=atoLFj3OD5KfXsa5dYBMC_mI06l068F5yZhF4jle1JA,14280 +pip/_internal/pep425tags.py,sha256=TQhxOPss4RjxgyVgxpSRe31HaTcWmn-LVjWBbkvkjzk,10845 +pip/_internal/pyproject.py,sha256=fpO52MCa3w5xSlXIBXw39BDTGzP8G4570EW34hVvIKQ,5481 +pip/_internal/req/__init__.py,sha256=JnNZWvKUQuqAwHh64LCD3zprzWIVQEXChTo2UGHzVqo,2093 +pip/_internal/req/__pycache__/__init__.cpython-37.pyc,, +pip/_internal/req/__pycache__/constructors.cpython-37.pyc,, +pip/_internal/req/__pycache__/req_file.cpython-37.pyc,, +pip/_internal/req/__pycache__/req_install.cpython-37.pyc,, +pip/_internal/req/__pycache__/req_set.cpython-37.pyc,, +pip/_internal/req/__pycache__/req_tracker.cpython-37.pyc,, +pip/_internal/req/__pycache__/req_uninstall.cpython-37.pyc,, +pip/_internal/req/constructors.py,sha256=97WQp9Svh-Jw3oLZL9_57gJ3zihm5LnWlSRjOwOorDU,9573 +pip/_internal/req/req_file.py,sha256=ORA0GKUjGd6vy7pmBwXR55FFj4h_OxYykFQ6gHuWvt0,11940 +pip/_internal/req/req_install.py,sha256=ry1RtNNCefDHAnf3EeGMpea-9pC6Yk1uHzP0Q5p2Un0,34046 +pip/_internal/req/req_set.py,sha256=nE6oagXJSiQREuuebX3oJO5OHSOVUIlvLLilodetBzc,7264 +pip/_internal/req/req_tracker.py,sha256=zH28YHV5TXAVh1ZOEZi6Z1Edkiu26dN2tXfR6VbQ3B4,2370 +pip/_internal/req/req_uninstall.py,sha256=ORSPah64KOVrKo-InMM3zgS5HQqbl5TLHFnE_Lxstq8,16737 +pip/_internal/resolve.py,sha256=tdepxCewsXXNFKSIYGSxiLvzi1xCv7UVFT9jRCDO90A,13578 +pip/_internal/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/utils/__pycache__/__init__.cpython-37.pyc,, +pip/_internal/utils/__pycache__/appdirs.cpython-37.pyc,, +pip/_internal/utils/__pycache__/compat.cpython-37.pyc,, +pip/_internal/utils/__pycache__/deprecation.cpython-37.pyc,, +pip/_internal/utils/__pycache__/encoding.cpython-37.pyc,, +pip/_internal/utils/__pycache__/filesystem.cpython-37.pyc,, +pip/_internal/utils/__pycache__/glibc.cpython-37.pyc,, +pip/_internal/utils/__pycache__/hashes.cpython-37.pyc,, +pip/_internal/utils/__pycache__/logging.cpython-37.pyc,, +pip/_internal/utils/__pycache__/misc.cpython-37.pyc,, +pip/_internal/utils/__pycache__/models.cpython-37.pyc,, +pip/_internal/utils/__pycache__/outdated.cpython-37.pyc,, +pip/_internal/utils/__pycache__/packaging.cpython-37.pyc,, +pip/_internal/utils/__pycache__/setuptools_build.cpython-37.pyc,, +pip/_internal/utils/__pycache__/temp_dir.cpython-37.pyc,, +pip/_internal/utils/__pycache__/typing.cpython-37.pyc,, +pip/_internal/utils/__pycache__/ui.cpython-37.pyc,, +pip/_internal/utils/appdirs.py,sha256=SPfibHtvOKzD_sHrpEZ60HfLae3GharU4Tg7SB3c-XM,9120 +pip/_internal/utils/compat.py,sha256=LSAvzXcsGY2O2drKIPszR5Ja2G0kup__51l3bx1jR_Q,8015 +pip/_internal/utils/deprecation.py,sha256=yQTe6dyWlBfxSBrOv_MdRXF1RPLER_EWOp-pa2zLoZc,3021 +pip/_internal/utils/encoding.py,sha256=D8tmfStCah6xh9OLhH9mWLr77q4akhg580YHJMKpq3Y,1025 +pip/_internal/utils/filesystem.py,sha256=ZOIHbacJ-SJtuZru4GoA5DuSIYyeaE4G5kfZPf5cn1A,915 +pip/_internal/utils/glibc.py,sha256=prOrsBjmgkDE-hY4Pl120yF5MIlkkmGrFLs8XfIyT-w,3004 +pip/_internal/utils/hashes.py,sha256=rJk-gj6F-sHggXAG97dhynqUHFFgApyZLWgaG2xCHME,2900 +pip/_internal/utils/logging.py,sha256=BQeUDEER3zlK0O4yv6DBfz6TK3f9XoLXyDlnB0mZVf0,6295 +pip/_internal/utils/misc.py,sha256=K5ouAkGO96le5zhngk_hSo7eysD-vMRYMqmkWnEaIFc,30639 +pip/_internal/utils/models.py,sha256=DQYZSRhjvSdDTAaJLLCpDtxAn1S_-v_8nlNjv4T2jwY,1042 +pip/_internal/utils/outdated.py,sha256=BXtCMKR6gjTrvMfP3MWzZ1Y4ZU4qqoCfbRNqQCusVt8,5642 +pip/_internal/utils/packaging.py,sha256=Ru8ls_S8PPKR8RKEn7jMetENY_A9jPet1HlhTZwpFxU,2443 +pip/_internal/utils/setuptools_build.py,sha256=0blfscmNJW_iZ5DcswJeDB_PbtTEjfK9RL1R1WEDW2E,278 +pip/_internal/utils/temp_dir.py,sha256=n2FkVlwRX_hS61fYt3nSAh2e2V6CcZn_dfbPId1pAQE,2615 +pip/_internal/utils/typing.py,sha256=ztYtZAcqjCYDwP-WlF6EiAAskAsZBMMXtuqvfgZIlgQ,1139 +pip/_internal/utils/ui.py,sha256=FW8wdtc7DvNwJClGr_TvGZlqcoO482GYe0UY9nKmpso,13657 +pip/_internal/vcs/__init__.py,sha256=2Ct9ogOwzS6ZKKaEXKN2XDiBOiFHMcejnN1KM21mLrQ,16319 +pip/_internal/vcs/__pycache__/__init__.cpython-37.pyc,, +pip/_internal/vcs/__pycache__/bazaar.cpython-37.pyc,, +pip/_internal/vcs/__pycache__/git.cpython-37.pyc,, +pip/_internal/vcs/__pycache__/mercurial.cpython-37.pyc,, +pip/_internal/vcs/__pycache__/subversion.cpython-37.pyc,, +pip/_internal/vcs/bazaar.py,sha256=rjskVmSSn68O7lC5JrGmDTWXneXFMMJJvj_bbdSM8QA,3669 +pip/_internal/vcs/git.py,sha256=n1cFBqTnLIcxAOClZMgOBqELjEjygDBPZ9z-Q7g0qVQ,12580 +pip/_internal/vcs/mercurial.py,sha256=jVTa0XQpFR6EiBcaqW4E4JjTce_t1tFnKRaIhaIPlS8,3471 +pip/_internal/vcs/subversion.py,sha256=vDLTfcjj0kgqcEsbPBfveC4CRxyhWiOjke-qN0Zr8CE,7676 +pip/_internal/wheel.py,sha256=fg9E936DaI1LyrBPHqtzHG_WEVyuUwipHISkD6N3jNw,32007 +pip/_vendor/__init__.py,sha256=bdhl7DUZ1z7eukZLktoO1vhki9sC576gBWcFgel4684,4890 +pip/_vendor/__pycache__/__init__.cpython-37.pyc,, +pip/_vendor/pep517/__init__.py,sha256=GH4HshnLERtjAjkY0zHoz3f7-35UcIvr27iFWSOUazU,82 +pip/_vendor/pep517/__pycache__/__init__.cpython-37.pyc,, +pip/_vendor/pep517/__pycache__/_in_process.cpython-37.pyc,, +pip/_vendor/pep517/__pycache__/check.cpython-37.pyc,, +pip/_vendor/pep517/__pycache__/colorlog.cpython-37.pyc,, +pip/_vendor/pep517/__pycache__/compat.cpython-37.pyc,, +pip/_vendor/pep517/__pycache__/envbuild.cpython-37.pyc,, +pip/_vendor/pep517/__pycache__/wrappers.cpython-37.pyc,, +pip/_vendor/pep517/_in_process.py,sha256=iWpagFk2GhNBbvl-Ca2RagfD0ALuits4WWSM6nQMTdg,5831 +pip/_vendor/pep517/check.py,sha256=Yp2NHW71DIOCgkFb7HKJOzKmsum_s_OokRP6HnR3bTg,5761 +pip/_vendor/pep517/colorlog.py,sha256=2AJuPI_DHM5T9IDgcTwf0E8suyHAFnfsesogr0AB7RQ,4048 +pip/_vendor/pep517/compat.py,sha256=4SFG4QN-cNj8ebSa0wV0HUtEEQWwmbok2a0uk1gYEOM,631 +pip/_vendor/pep517/envbuild.py,sha256=osRsJVd7hir1w_uFXiVeeWxfJ3iYhwxsKRgNBWpqtCI,5672 +pip/_vendor/pep517/wrappers.py,sha256=RhgWm-MLxpYPgc9cZ3-A3ToN99ZzgM8-ia4FDB58koM,5018 diff --git a/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/WHEEL b/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/WHEEL new file mode 100644 index 0000000..c4bde30 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.32.3) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/entry_points.txt b/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/entry_points.txt new file mode 100644 index 0000000..f5809cb --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/entry_points.txt @@ -0,0 +1,5 @@ +[console_scripts] +pip = pip._internal:main +pip3 = pip._internal:main +pip3.7 = pip._internal:main + diff --git a/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/top_level.txt b/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/top_level.txt new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip-18.1.dist-info/top_level.txt @@ -0,0 +1 @@ +pip diff --git a/Display/.venv/lib/python3.7/site-packages/pip/__init__.py b/Display/.venv/lib/python3.7/site-packages/pip/__init__.py new file mode 100644 index 0000000..ae265fa --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/__init__.py @@ -0,0 +1 @@ +__version__ = "18.1" diff --git a/Display/.venv/lib/python3.7/site-packages/pip/__main__.py b/Display/.venv/lib/python3.7/site-packages/pip/__main__.py new file mode 100644 index 0000000..0c223f8 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/__main__.py @@ -0,0 +1,19 @@ +from __future__ import absolute_import + +import os +import sys + +# If we are running from a wheel, add the wheel to sys.path +# This allows the usage python pip-*.whl/pip install pip-*.whl +if __package__ == '': + # __file__ is pip-*.whl/pip/__main__.py + # first dirname call strips of '/__main__.py', second strips off '/pip' + # Resulting path is the name of the wheel itself + # Add that to sys.path so we can import pip + path = os.path.dirname(os.path.dirname(__file__)) + sys.path.insert(0, path) + +from pip._internal import main as _main # isort:skip # noqa + +if __name__ == '__main__': + sys.exit(_main()) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/__init__.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/__init__.py new file mode 100644 index 0000000..276124d --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/__init__.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +from __future__ import absolute_import + +import locale +import logging +import os +import warnings + +import sys + +# 2016-06-17 barry@debian.org: urllib3 1.14 added optional support for socks, +# but if invoked (i.e. imported), it will issue a warning to stderr if socks +# isn't available. requests unconditionally imports urllib3's socks contrib +# module, triggering this warning. The warning breaks DEP-8 tests (because of +# the stderr output) and is just plain annoying in normal usage. I don't want +# to add socks as yet another dependency for pip, nor do I want to allow-stder +# in the DEP-8 tests, so just suppress the warning. pdb tells me this has to +# be done before the import of pip.vcs. +from pip._vendor.urllib3.exceptions import DependencyWarning +warnings.filterwarnings("ignore", category=DependencyWarning) # noqa + +# We want to inject the use of SecureTransport as early as possible so that any +# references or sessions or what have you are ensured to have it, however we +# only want to do this in the case that we're running on macOS and the linked +# OpenSSL is too old to handle TLSv1.2 +try: + import ssl +except ImportError: + pass +else: + # Checks for OpenSSL 1.0.1 on MacOS + if sys.platform == "darwin" and ssl.OPENSSL_VERSION_NUMBER < 0x1000100f: + try: + from pip._vendor.urllib3.contrib import securetransport + except (ImportError, OSError): + pass + else: + securetransport.inject_into_urllib3() + +from pip._internal.cli.autocompletion import autocomplete +from pip._internal.cli.main_parser import parse_command +from pip._internal.commands import commands_dict +from pip._internal.exceptions import PipError +from pip._internal.utils import deprecation +from pip._internal.vcs import git, mercurial, subversion, bazaar # noqa +from pip._vendor.urllib3.exceptions import InsecureRequestWarning + +logger = logging.getLogger(__name__) + +# Hide the InsecureRequestWarning from urllib3 +warnings.filterwarnings("ignore", category=InsecureRequestWarning) + + +def main(args=None): + if args is None: + args = sys.argv[1:] + + # Configure our deprecation warnings to be sent through loggers + deprecation.install_warning_logger() + + autocomplete() + + try: + cmd_name, cmd_args = parse_command(args) + except PipError as exc: + sys.stderr.write("ERROR: %s" % exc) + sys.stderr.write(os.linesep) + sys.exit(1) + + # Needed for locale.getpreferredencoding(False) to work + # in pip._internal.utils.encoding.auto_decode + try: + locale.setlocale(locale.LC_ALL, '') + except locale.Error as e: + # setlocale can apparently crash if locale are uninitialized + logger.debug("Ignoring error %s when setting locale", e) + command = commands_dict[cmd_name](isolated=("--isolated" in cmd_args)) + return command.main(cmd_args) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/build_env.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/build_env.py new file mode 100644 index 0000000..673409d --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/build_env.py @@ -0,0 +1,142 @@ +"""Build Environment used for isolation during sdist building +""" + +import logging +import os +import sys +from distutils.sysconfig import get_python_lib +from sysconfig import get_paths + +from pip._vendor.pkg_resources import Requirement, VersionConflict, WorkingSet + +from pip._internal.utils.misc import call_subprocess +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.ui import open_spinner + +logger = logging.getLogger(__name__) + + +class BuildEnvironment(object): + """Creates and manages an isolated environment to install build deps + """ + + def __init__(self): + self._temp_dir = TempDirectory(kind="build-env") + self._temp_dir.create() + + @property + def path(self): + return self._temp_dir.path + + def __enter__(self): + self.save_path = os.environ.get('PATH', None) + self.save_pythonpath = os.environ.get('PYTHONPATH', None) + self.save_nousersite = os.environ.get('PYTHONNOUSERSITE', None) + + install_scheme = 'nt' if (os.name == 'nt') else 'posix_prefix' + install_dirs = get_paths(install_scheme, vars={ + 'base': self.path, + 'platbase': self.path, + }) + + scripts = install_dirs['scripts'] + if self.save_path: + os.environ['PATH'] = scripts + os.pathsep + self.save_path + else: + os.environ['PATH'] = scripts + os.pathsep + os.defpath + + # Note: prefer distutils' sysconfig to get the + # library paths so PyPy is correctly supported. + purelib = get_python_lib(plat_specific=0, prefix=self.path) + platlib = get_python_lib(plat_specific=1, prefix=self.path) + if purelib == platlib: + lib_dirs = purelib + else: + lib_dirs = purelib + os.pathsep + platlib + if self.save_pythonpath: + os.environ['PYTHONPATH'] = lib_dirs + os.pathsep + \ + self.save_pythonpath + else: + os.environ['PYTHONPATH'] = lib_dirs + + os.environ['PYTHONNOUSERSITE'] = '1' + + return self.path + + def __exit__(self, exc_type, exc_val, exc_tb): + def restore_var(varname, old_value): + if old_value is None: + os.environ.pop(varname, None) + else: + os.environ[varname] = old_value + + restore_var('PATH', self.save_path) + restore_var('PYTHONPATH', self.save_pythonpath) + restore_var('PYTHONNOUSERSITE', self.save_nousersite) + + def cleanup(self): + self._temp_dir.cleanup() + + def missing_requirements(self, reqs): + """Return a list of the requirements from reqs that are not present + """ + missing = [] + with self: + ws = WorkingSet(os.environ["PYTHONPATH"].split(os.pathsep)) + for req in reqs: + try: + if ws.find(Requirement.parse(req)) is None: + missing.append(req) + except VersionConflict: + missing.append(req) + return missing + + def install_requirements(self, finder, requirements, message): + args = [ + sys.executable, '-m', 'pip', 'install', '--ignore-installed', + '--no-user', '--prefix', self.path, '--no-warn-script-location', + ] + if logger.getEffectiveLevel() <= logging.DEBUG: + args.append('-v') + for format_control in ('no_binary', 'only_binary'): + formats = getattr(finder.format_control, format_control) + args.extend(('--' + format_control.replace('_', '-'), + ','.join(sorted(formats or {':none:'})))) + if finder.index_urls: + args.extend(['-i', finder.index_urls[0]]) + for extra_index in finder.index_urls[1:]: + args.extend(['--extra-index-url', extra_index]) + else: + args.append('--no-index') + for link in finder.find_links: + args.extend(['--find-links', link]) + for _, host, _ in finder.secure_origins: + args.extend(['--trusted-host', host]) + if finder.allow_all_prereleases: + args.append('--pre') + if finder.process_dependency_links: + args.append('--process-dependency-links') + args.append('--') + args.extend(requirements) + with open_spinner(message) as spinner: + call_subprocess(args, show_stdout=False, spinner=spinner) + + +class NoOpBuildEnvironment(BuildEnvironment): + """A no-op drop-in replacement for BuildEnvironment + """ + + def __init__(self): + pass + + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + def cleanup(self): + pass + + def install_requirements(self, finder, requirements, message): + raise NotImplementedError() diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/cache.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/cache.py new file mode 100644 index 0000000..33bec97 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/cache.py @@ -0,0 +1,202 @@ +"""Cache Management +""" + +import errno +import hashlib +import logging +import os + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.download import path_to_url +from pip._internal.models.link import Link +from pip._internal.utils.compat import expanduser +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.wheel import InvalidWheelFilename, Wheel + +logger = logging.getLogger(__name__) + + +class Cache(object): + """An abstract class - provides cache directories for data from links + + + :param cache_dir: The root of the cache. + :param format_control: An object of FormatControl class to limit + binaries being read from the cache. + :param allowed_formats: which formats of files the cache should store. + ('binary' and 'source' are the only allowed values) + """ + + def __init__(self, cache_dir, format_control, allowed_formats): + super(Cache, self).__init__() + self.cache_dir = expanduser(cache_dir) if cache_dir else None + self.format_control = format_control + self.allowed_formats = allowed_formats + + _valid_formats = {"source", "binary"} + assert self.allowed_formats.union(_valid_formats) == _valid_formats + + def _get_cache_path_parts(self, link): + """Get parts of part that must be os.path.joined with cache_dir + """ + + # We want to generate an url to use as our cache key, we don't want to + # just re-use the URL because it might have other items in the fragment + # and we don't care about those. + key_parts = [link.url_without_fragment] + if link.hash_name is not None and link.hash is not None: + key_parts.append("=".join([link.hash_name, link.hash])) + key_url = "#".join(key_parts) + + # Encode our key url with sha224, we'll use this because it has similar + # security properties to sha256, but with a shorter total output (and + # thus less secure). However the differences don't make a lot of + # difference for our use case here. + hashed = hashlib.sha224(key_url.encode()).hexdigest() + + # We want to nest the directories some to prevent having a ton of top + # level directories where we might run out of sub directories on some + # FS. + parts = [hashed[:2], hashed[2:4], hashed[4:6], hashed[6:]] + + return parts + + def _get_candidates(self, link, package_name): + can_not_cache = ( + not self.cache_dir or + not package_name or + not link + ) + if can_not_cache: + return [] + + canonical_name = canonicalize_name(package_name) + formats = self.format_control.get_allowed_formats( + canonical_name + ) + if not self.allowed_formats.intersection(formats): + return [] + + root = self.get_path_for_link(link) + try: + return os.listdir(root) + except OSError as err: + if err.errno in {errno.ENOENT, errno.ENOTDIR}: + return [] + raise + + def get_path_for_link(self, link): + """Return a directory to store cached items in for link. + """ + raise NotImplementedError() + + def get(self, link, package_name): + """Returns a link to a cached item if it exists, otherwise returns the + passed link. + """ + raise NotImplementedError() + + def _link_for_candidate(self, link, candidate): + root = self.get_path_for_link(link) + path = os.path.join(root, candidate) + + return Link(path_to_url(path)) + + def cleanup(self): + pass + + +class SimpleWheelCache(Cache): + """A cache of wheels for future installs. + """ + + def __init__(self, cache_dir, format_control): + super(SimpleWheelCache, self).__init__( + cache_dir, format_control, {"binary"} + ) + + def get_path_for_link(self, link): + """Return a directory to store cached wheels for link + + Because there are M wheels for any one sdist, we provide a directory + to cache them in, and then consult that directory when looking up + cache hits. + + We only insert things into the cache if they have plausible version + numbers, so that we don't contaminate the cache with things that were + not unique. E.g. ./package might have dozens of installs done for it + and build a version of 0.0...and if we built and cached a wheel, we'd + end up using the same wheel even if the source has been edited. + + :param link: The link of the sdist for which this will cache wheels. + """ + parts = self._get_cache_path_parts(link) + + # Store wheels within the root cache_dir + return os.path.join(self.cache_dir, "wheels", *parts) + + def get(self, link, package_name): + candidates = [] + + for wheel_name in self._get_candidates(link, package_name): + try: + wheel = Wheel(wheel_name) + except InvalidWheelFilename: + continue + if not wheel.supported(): + # Built for a different python/arch/etc + continue + candidates.append((wheel.support_index_min(), wheel_name)) + + if not candidates: + return link + + return self._link_for_candidate(link, min(candidates)[1]) + + +class EphemWheelCache(SimpleWheelCache): + """A SimpleWheelCache that creates it's own temporary cache directory + """ + + def __init__(self, format_control): + self._temp_dir = TempDirectory(kind="ephem-wheel-cache") + self._temp_dir.create() + + super(EphemWheelCache, self).__init__( + self._temp_dir.path, format_control + ) + + def cleanup(self): + self._temp_dir.cleanup() + + +class WheelCache(Cache): + """Wraps EphemWheelCache and SimpleWheelCache into a single Cache + + This Cache allows for gracefully degradation, using the ephem wheel cache + when a certain link is not found in the simple wheel cache first. + """ + + def __init__(self, cache_dir, format_control): + super(WheelCache, self).__init__( + cache_dir, format_control, {'binary'} + ) + self._wheel_cache = SimpleWheelCache(cache_dir, format_control) + self._ephem_cache = EphemWheelCache(format_control) + + def get_path_for_link(self, link): + return self._wheel_cache.get_path_for_link(link) + + def get_ephem_path_for_link(self, link): + return self._ephem_cache.get_path_for_link(link) + + def get(self, link, package_name): + retval = self._wheel_cache.get(link, package_name) + if retval is link: + retval = self._ephem_cache.get(link, package_name) + return retval + + def cleanup(self): + self._wheel_cache.cleanup() + self._ephem_cache.cleanup() diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/__init__.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/__init__.py new file mode 100644 index 0000000..e589bb9 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/__init__.py @@ -0,0 +1,4 @@ +"""Subpackage containing all of pip's command line interface related code +""" + +# This file intentionally does not import submodules diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/autocompletion.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/autocompletion.py new file mode 100644 index 0000000..0a04199 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/autocompletion.py @@ -0,0 +1,152 @@ +"""Logic that powers autocompletion installed by ``pip completion``. +""" + +import optparse +import os +import sys + +from pip._internal.cli.main_parser import create_main_parser +from pip._internal.commands import commands_dict, get_summaries +from pip._internal.utils.misc import get_installed_distributions + + +def autocomplete(): + """Entry Point for completion of main and subcommand options. + """ + # Don't complete if user hasn't sourced bash_completion file. + if 'PIP_AUTO_COMPLETE' not in os.environ: + return + cwords = os.environ['COMP_WORDS'].split()[1:] + cword = int(os.environ['COMP_CWORD']) + try: + current = cwords[cword - 1] + except IndexError: + current = '' + + subcommands = [cmd for cmd, summary in get_summaries()] + options = [] + # subcommand + try: + subcommand_name = [w for w in cwords if w in subcommands][0] + except IndexError: + subcommand_name = None + + parser = create_main_parser() + # subcommand options + if subcommand_name: + # special case: 'help' subcommand has no options + if subcommand_name == 'help': + sys.exit(1) + # special case: list locally installed dists for show and uninstall + should_list_installed = ( + subcommand_name in ['show', 'uninstall'] and + not current.startswith('-') + ) + if should_list_installed: + installed = [] + lc = current.lower() + for dist in get_installed_distributions(local_only=True): + if dist.key.startswith(lc) and dist.key not in cwords[1:]: + installed.append(dist.key) + # if there are no dists installed, fall back to option completion + if installed: + for dist in installed: + print(dist) + sys.exit(1) + + subcommand = commands_dict[subcommand_name]() + + for opt in subcommand.parser.option_list_all: + if opt.help != optparse.SUPPRESS_HELP: + for opt_str in opt._long_opts + opt._short_opts: + options.append((opt_str, opt.nargs)) + + # filter out previously specified options from available options + prev_opts = [x.split('=')[0] for x in cwords[1:cword - 1]] + options = [(x, v) for (x, v) in options if x not in prev_opts] + # filter options by current input + options = [(k, v) for k, v in options if k.startswith(current)] + # get completion type given cwords and available subcommand options + completion_type = get_path_completion_type( + cwords, cword, subcommand.parser.option_list_all, + ) + # get completion files and directories if ``completion_type`` is + # ````, ```` or ```` + if completion_type: + options = auto_complete_paths(current, completion_type) + options = ((opt, 0) for opt in options) + for option in options: + opt_label = option[0] + # append '=' to options which require args + if option[1] and option[0][:2] == "--": + opt_label += '=' + print(opt_label) + else: + # show main parser options only when necessary + + opts = [i.option_list for i in parser.option_groups] + opts.append(parser.option_list) + opts = (o for it in opts for o in it) + if current.startswith('-'): + for opt in opts: + if opt.help != optparse.SUPPRESS_HELP: + subcommands += opt._long_opts + opt._short_opts + else: + # get completion type given cwords and all available options + completion_type = get_path_completion_type(cwords, cword, opts) + if completion_type: + subcommands = auto_complete_paths(current, completion_type) + + print(' '.join([x for x in subcommands if x.startswith(current)])) + sys.exit(1) + + +def get_path_completion_type(cwords, cword, opts): + """Get the type of path completion (``file``, ``dir``, ``path`` or None) + + :param cwords: same as the environmental variable ``COMP_WORDS`` + :param cword: same as the environmental variable ``COMP_CWORD`` + :param opts: The available options to check + :return: path completion type (``file``, ``dir``, ``path`` or None) + """ + if cword < 2 or not cwords[cword - 2].startswith('-'): + return + for opt in opts: + if opt.help == optparse.SUPPRESS_HELP: + continue + for o in str(opt).split('/'): + if cwords[cword - 2].split('=')[0] == o: + if not opt.metavar or any( + x in ('path', 'file', 'dir') + for x in opt.metavar.split('/')): + return opt.metavar + + +def auto_complete_paths(current, completion_type): + """If ``completion_type`` is ``file`` or ``path``, list all regular files + and directories starting with ``current``; otherwise only list directories + starting with ``current``. + + :param current: The word to be completed + :param completion_type: path completion type(`file`, `path` or `dir`)i + :return: A generator of regular files and/or directories + """ + directory, filename = os.path.split(current) + current_path = os.path.abspath(directory) + # Don't complete paths if they can't be accessed + if not os.access(current_path, os.R_OK): + return + filename = os.path.normcase(filename) + # list all files that start with ``filename`` + file_list = (x for x in os.listdir(current_path) + if os.path.normcase(x).startswith(filename)) + for f in file_list: + opt = os.path.join(current_path, f) + comp_file = os.path.normcase(os.path.join(directory, f)) + # complete regular files when there is not ```` after option + # complete directories when there is ````, ```` or + # ````after option + if completion_type != 'dir' and os.path.isfile(opt): + yield comp_file + elif os.path.isdir(opt): + yield os.path.join(comp_file, '') diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/base_command.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/base_command.py new file mode 100644 index 0000000..dac4b05 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/base_command.py @@ -0,0 +1,278 @@ +"""Base Command class, and related routines""" +from __future__ import absolute_import + +import logging +import logging.config +import optparse +import os +import sys + +from pip._internal.cli import cmdoptions +from pip._internal.cli.parser import ( + ConfigOptionParser, UpdatingDefaultsHelpFormatter, +) +from pip._internal.cli.status_codes import ( + ERROR, PREVIOUS_BUILD_DIR_ERROR, SUCCESS, UNKNOWN_ERROR, + VIRTUALENV_NOT_FOUND, +) +from pip._internal.download import PipSession +from pip._internal.exceptions import ( + BadCommand, CommandError, InstallationError, PreviousBuildDirError, + UninstallationError, +) +from pip._internal.index import PackageFinder +from pip._internal.locations import running_under_virtualenv +from pip._internal.req.constructors import ( + install_req_from_editable, install_req_from_line, +) +from pip._internal.req.req_file import parse_requirements +from pip._internal.utils.logging import setup_logging +from pip._internal.utils.misc import get_prog, normalize_path +from pip._internal.utils.outdated import pip_version_check +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional # noqa: F401 + +__all__ = ['Command'] + +logger = logging.getLogger(__name__) + + +class Command(object): + name = None # type: Optional[str] + usage = None # type: Optional[str] + hidden = False # type: bool + ignore_require_venv = False # type: bool + + def __init__(self, isolated=False): + parser_kw = { + 'usage': self.usage, + 'prog': '%s %s' % (get_prog(), self.name), + 'formatter': UpdatingDefaultsHelpFormatter(), + 'add_help_option': False, + 'name': self.name, + 'description': self.__doc__, + 'isolated': isolated, + } + + self.parser = ConfigOptionParser(**parser_kw) + + # Commands should add options to this option group + optgroup_name = '%s Options' % self.name.capitalize() + self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name) + + # Add the general options + gen_opts = cmdoptions.make_option_group( + cmdoptions.general_group, + self.parser, + ) + self.parser.add_option_group(gen_opts) + + def _build_session(self, options, retries=None, timeout=None): + session = PipSession( + cache=( + normalize_path(os.path.join(options.cache_dir, "http")) + if options.cache_dir else None + ), + retries=retries if retries is not None else options.retries, + insecure_hosts=options.trusted_hosts, + ) + + # Handle custom ca-bundles from the user + if options.cert: + session.verify = options.cert + + # Handle SSL client certificate + if options.client_cert: + session.cert = options.client_cert + + # Handle timeouts + if options.timeout or timeout: + session.timeout = ( + timeout if timeout is not None else options.timeout + ) + + # Handle configured proxies + if options.proxy: + session.proxies = { + "http": options.proxy, + "https": options.proxy, + } + + # Determine if we can prompt the user for authentication or not + session.auth.prompting = not options.no_input + + return session + + def parse_args(self, args): + # factored out for testability + return self.parser.parse_args(args) + + def main(self, args): + options, args = self.parse_args(args) + + # Set verbosity so that it can be used elsewhere. + self.verbosity = options.verbose - options.quiet + + setup_logging( + verbosity=self.verbosity, + no_color=options.no_color, + user_log_file=options.log, + ) + + # TODO: Try to get these passing down from the command? + # without resorting to os.environ to hold these. + # This also affects isolated builds and it should. + + if options.no_input: + os.environ['PIP_NO_INPUT'] = '1' + + if options.exists_action: + os.environ['PIP_EXISTS_ACTION'] = ' '.join(options.exists_action) + + if options.require_venv and not self.ignore_require_venv: + # If a venv is required check if it can really be found + if not running_under_virtualenv(): + logger.critical( + 'Could not find an activated virtualenv (required).' + ) + sys.exit(VIRTUALENV_NOT_FOUND) + + try: + status = self.run(options, args) + # FIXME: all commands should return an exit status + # and when it is done, isinstance is not needed anymore + if isinstance(status, int): + return status + except PreviousBuildDirError as exc: + logger.critical(str(exc)) + logger.debug('Exception information:', exc_info=True) + + return PREVIOUS_BUILD_DIR_ERROR + except (InstallationError, UninstallationError, BadCommand) as exc: + logger.critical(str(exc)) + logger.debug('Exception information:', exc_info=True) + + return ERROR + except CommandError as exc: + logger.critical('ERROR: %s', exc) + logger.debug('Exception information:', exc_info=True) + + return ERROR + except KeyboardInterrupt: + logger.critical('Operation cancelled by user') + logger.debug('Exception information:', exc_info=True) + + return ERROR + except BaseException: + logger.critical('Exception:', exc_info=True) + + return UNKNOWN_ERROR + finally: + allow_version_check = ( + # Does this command have the index_group options? + hasattr(options, "no_index") and + # Is this command allowed to perform this check? + not (options.disable_pip_version_check or options.no_index) + ) + # Check if we're using the latest version of pip available + if allow_version_check: + session = self._build_session( + options, + retries=0, + timeout=min(5, options.timeout) + ) + with session: + pip_version_check(session, options) + + # Shutdown the logging module + logging.shutdown() + + return SUCCESS + + +class RequirementCommand(Command): + + @staticmethod + def populate_requirement_set(requirement_set, args, options, finder, + session, name, wheel_cache): + """ + Marshal cmd line args into a requirement set. + """ + # NOTE: As a side-effect, options.require_hashes and + # requirement_set.require_hashes may be updated + + for filename in options.constraints: + for req_to_add in parse_requirements( + filename, + constraint=True, finder=finder, options=options, + session=session, wheel_cache=wheel_cache): + req_to_add.is_direct = True + requirement_set.add_requirement(req_to_add) + + for req in args: + req_to_add = install_req_from_line( + req, None, isolated=options.isolated_mode, + wheel_cache=wheel_cache + ) + req_to_add.is_direct = True + requirement_set.add_requirement(req_to_add) + + for req in options.editables: + req_to_add = install_req_from_editable( + req, + isolated=options.isolated_mode, + wheel_cache=wheel_cache + ) + req_to_add.is_direct = True + requirement_set.add_requirement(req_to_add) + + for filename in options.requirements: + for req_to_add in parse_requirements( + filename, + finder=finder, options=options, session=session, + wheel_cache=wheel_cache): + req_to_add.is_direct = True + requirement_set.add_requirement(req_to_add) + # If --require-hashes was a line in a requirements file, tell + # RequirementSet about it: + requirement_set.require_hashes = options.require_hashes + + if not (args or options.editables or options.requirements): + opts = {'name': name} + if options.find_links: + raise CommandError( + 'You must give at least one requirement to %(name)s ' + '(maybe you meant "pip %(name)s %(links)s"?)' % + dict(opts, links=' '.join(options.find_links))) + else: + raise CommandError( + 'You must give at least one requirement to %(name)s ' + '(see "pip help %(name)s")' % opts) + + def _build_package_finder(self, options, session, + platform=None, python_versions=None, + abi=None, implementation=None): + """ + Create a package finder appropriate to this requirement command. + """ + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index: + logger.debug('Ignoring indexes: %s', ','.join(index_urls)) + index_urls = [] + + return PackageFinder( + find_links=options.find_links, + format_control=options.format_control, + index_urls=index_urls, + trusted_hosts=options.trusted_hosts, + allow_all_prereleases=options.pre, + process_dependency_links=options.process_dependency_links, + session=session, + platform=platform, + versions=python_versions, + abi=abi, + implementation=implementation, + prefer_binary=options.prefer_binary, + ) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/cmdoptions.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/cmdoptions.py new file mode 100644 index 0000000..29b758f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/cmdoptions.py @@ -0,0 +1,714 @@ +""" +shared options and groups + +The principle here is to define options once, but *not* instantiate them +globally. One reason being that options with action='append' can carry state +between parses. pip parses general options twice internally, and shouldn't +pass on state. To be consistent, all options will follow this design. + +""" +from __future__ import absolute_import + +import warnings +from functools import partial +from optparse import SUPPRESS_HELP, Option, OptionGroup + +from pip._internal.exceptions import CommandError +from pip._internal.locations import USER_CACHE_DIR, src_prefix +from pip._internal.models.format_control import FormatControl +from pip._internal.models.index import PyPI +from pip._internal.utils.hashes import STRONG_HASHES +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.ui import BAR_TYPES + +if MYPY_CHECK_RUNNING: + from typing import Any # noqa: F401 + + +def make_option_group(group, parser): + """ + Return an OptionGroup object + group -- assumed to be dict with 'name' and 'options' keys + parser -- an optparse Parser + """ + option_group = OptionGroup(parser, group['name']) + for option in group['options']: + option_group.add_option(option()) + return option_group + + +def check_install_build_global(options, check_options=None): + """Disable wheels if per-setup.py call options are set. + + :param options: The OptionParser options to update. + :param check_options: The options to check, if not supplied defaults to + options. + """ + if check_options is None: + check_options = options + + def getname(n): + return getattr(check_options, n, None) + names = ["build_options", "global_options", "install_options"] + if any(map(getname, names)): + control = options.format_control + control.disallow_binaries() + warnings.warn( + 'Disabling all use of wheels due to the use of --build-options ' + '/ --global-options / --install-options.', stacklevel=2, + ) + + +def check_dist_restriction(options, check_target=False): + """Function for determining if custom platform options are allowed. + + :param options: The OptionParser options. + :param check_target: Whether or not to check if --target is being used. + """ + dist_restriction_set = any([ + options.python_version, + options.platform, + options.abi, + options.implementation, + ]) + + binary_only = FormatControl(set(), {':all:'}) + sdist_dependencies_allowed = ( + options.format_control != binary_only and + not options.ignore_dependencies + ) + + # Installations or downloads using dist restrictions must not combine + # source distributions and dist-specific wheels, as they are not + # gauranteed to be locally compatible. + if dist_restriction_set and sdist_dependencies_allowed: + raise CommandError( + "When restricting platform and interpreter constraints using " + "--python-version, --platform, --abi, or --implementation, " + "either --no-deps must be set, or --only-binary=:all: must be " + "set and --no-binary must not be set (or must be set to " + ":none:)." + ) + + if check_target: + if dist_restriction_set and not options.target_dir: + raise CommandError( + "Can not use any platform or abi specific options unless " + "installing via '--target'" + ) + + +########### +# options # +########### + +help_ = partial( + Option, + '-h', '--help', + dest='help', + action='help', + help='Show help.', +) # type: Any + +isolated_mode = partial( + Option, + "--isolated", + dest="isolated_mode", + action="store_true", + default=False, + help=( + "Run pip in an isolated mode, ignoring environment variables and user " + "configuration." + ), +) + +require_virtualenv = partial( + Option, + # Run only if inside a virtualenv, bail if not. + '--require-virtualenv', '--require-venv', + dest='require_venv', + action='store_true', + default=False, + help=SUPPRESS_HELP +) # type: Any + +verbose = partial( + Option, + '-v', '--verbose', + dest='verbose', + action='count', + default=0, + help='Give more output. Option is additive, and can be used up to 3 times.' +) + +no_color = partial( + Option, + '--no-color', + dest='no_color', + action='store_true', + default=False, + help="Suppress colored output", +) + +version = partial( + Option, + '-V', '--version', + dest='version', + action='store_true', + help='Show version and exit.', +) # type: Any + +quiet = partial( + Option, + '-q', '--quiet', + dest='quiet', + action='count', + default=0, + help=( + 'Give less output. Option is additive, and can be used up to 3' + ' times (corresponding to WARNING, ERROR, and CRITICAL logging' + ' levels).' + ), +) # type: Any + +progress_bar = partial( + Option, + '--progress-bar', + dest='progress_bar', + type='choice', + choices=list(BAR_TYPES.keys()), + default='on', + help=( + 'Specify type of progress to be displayed [' + + '|'.join(BAR_TYPES.keys()) + '] (default: %default)' + ), +) # type: Any + +log = partial( + Option, + "--log", "--log-file", "--local-log", + dest="log", + metavar="path", + help="Path to a verbose appending log." +) # type: Any + +no_input = partial( + Option, + # Don't ask for input + '--no-input', + dest='no_input', + action='store_true', + default=False, + help=SUPPRESS_HELP +) # type: Any + +proxy = partial( + Option, + '--proxy', + dest='proxy', + type='str', + default='', + help="Specify a proxy in the form [user:passwd@]proxy.server:port." +) # type: Any + +retries = partial( + Option, + '--retries', + dest='retries', + type='int', + default=5, + help="Maximum number of retries each connection should attempt " + "(default %default times).", +) # type: Any + +timeout = partial( + Option, + '--timeout', '--default-timeout', + metavar='sec', + dest='timeout', + type='float', + default=15, + help='Set the socket timeout (default %default seconds).', +) # type: Any + +skip_requirements_regex = partial( + Option, + # A regex to be used to skip requirements + '--skip-requirements-regex', + dest='skip_requirements_regex', + type='str', + default='', + help=SUPPRESS_HELP, +) # type: Any + + +def exists_action(): + return Option( + # Option when path already exist + '--exists-action', + dest='exists_action', + type='choice', + choices=['s', 'i', 'w', 'b', 'a'], + default=[], + action='append', + metavar='action', + help="Default action when a path already exists: " + "(s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort).", + ) + + +cert = partial( + Option, + '--cert', + dest='cert', + type='str', + metavar='path', + help="Path to alternate CA bundle.", +) # type: Any + +client_cert = partial( + Option, + '--client-cert', + dest='client_cert', + type='str', + default=None, + metavar='path', + help="Path to SSL client certificate, a single file containing the " + "private key and the certificate in PEM format.", +) # type: Any + +index_url = partial( + Option, + '-i', '--index-url', '--pypi-url', + dest='index_url', + metavar='URL', + default=PyPI.simple_url, + help="Base URL of Python Package Index (default %default). " + "This should point to a repository compliant with PEP 503 " + "(the simple repository API) or a local directory laid out " + "in the same format.", +) # type: Any + + +def extra_index_url(): + return Option( + '--extra-index-url', + dest='extra_index_urls', + metavar='URL', + action='append', + default=[], + help="Extra URLs of package indexes to use in addition to " + "--index-url. Should follow the same rules as " + "--index-url.", + ) + + +no_index = partial( + Option, + '--no-index', + dest='no_index', + action='store_true', + default=False, + help='Ignore package index (only looking at --find-links URLs instead).', +) # type: Any + + +def find_links(): + return Option( + '-f', '--find-links', + dest='find_links', + action='append', + default=[], + metavar='url', + help="If a url or path to an html file, then parse for links to " + "archives. If a local path or file:// url that's a directory, " + "then look for archives in the directory listing.", + ) + + +def trusted_host(): + return Option( + "--trusted-host", + dest="trusted_hosts", + action="append", + metavar="HOSTNAME", + default=[], + help="Mark this host as trusted, even though it does not have valid " + "or any HTTPS.", + ) + + +# Remove after 1.5 +process_dependency_links = partial( + Option, + "--process-dependency-links", + dest="process_dependency_links", + action="store_true", + default=False, + help="Enable the processing of dependency links.", +) # type: Any + + +def constraints(): + return Option( + '-c', '--constraint', + dest='constraints', + action='append', + default=[], + metavar='file', + help='Constrain versions using the given constraints file. ' + 'This option can be used multiple times.' + ) + + +def requirements(): + return Option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help='Install from the given requirements file. ' + 'This option can be used multiple times.' + ) + + +def editable(): + return Option( + '-e', '--editable', + dest='editables', + action='append', + default=[], + metavar='path/url', + help=('Install a project in editable mode (i.e. setuptools ' + '"develop mode") from a local project path or a VCS url.'), + ) + + +src = partial( + Option, + '--src', '--source', '--source-dir', '--source-directory', + dest='src_dir', + metavar='dir', + default=src_prefix, + help='Directory to check out editable projects into. ' + 'The default in a virtualenv is "/src". ' + 'The default for global installs is "/src".' +) # type: Any + + +def _get_format_control(values, option): + """Get a format_control object.""" + return getattr(values, option.dest) + + +def _handle_no_binary(option, opt_str, value, parser): + existing = _get_format_control(parser.values, option) + FormatControl.handle_mutual_excludes( + value, existing.no_binary, existing.only_binary, + ) + + +def _handle_only_binary(option, opt_str, value, parser): + existing = _get_format_control(parser.values, option) + FormatControl.handle_mutual_excludes( + value, existing.only_binary, existing.no_binary, + ) + + +def no_binary(): + format_control = FormatControl(set(), set()) + return Option( + "--no-binary", dest="format_control", action="callback", + callback=_handle_no_binary, type="str", + default=format_control, + help="Do not use binary packages. Can be supplied multiple times, and " + "each time adds to the existing value. Accepts either :all: to " + "disable all binary packages, :none: to empty the set, or one or " + "more package names with commas between them. Note that some " + "packages are tricky to compile and may fail to install when " + "this option is used on them.", + ) + + +def only_binary(): + format_control = FormatControl(set(), set()) + return Option( + "--only-binary", dest="format_control", action="callback", + callback=_handle_only_binary, type="str", + default=format_control, + help="Do not use source packages. Can be supplied multiple times, and " + "each time adds to the existing value. Accepts either :all: to " + "disable all source packages, :none: to empty the set, or one or " + "more package names with commas between them. Packages without " + "binary distributions will fail to install when this option is " + "used on them.", + ) + + +platform = partial( + Option, + '--platform', + dest='platform', + metavar='platform', + default=None, + help=("Only use wheels compatible with . " + "Defaults to the platform of the running system."), +) + + +python_version = partial( + Option, + '--python-version', + dest='python_version', + metavar='python_version', + default=None, + help=("Only use wheels compatible with Python " + "interpreter version . If not specified, then the " + "current system interpreter minor version is used. A major " + "version (e.g. '2') can be specified to match all " + "minor revs of that major version. A minor version " + "(e.g. '34') can also be specified."), +) + + +implementation = partial( + Option, + '--implementation', + dest='implementation', + metavar='implementation', + default=None, + help=("Only use wheels compatible with Python " + "implementation , e.g. 'pp', 'jy', 'cp', " + " or 'ip'. If not specified, then the current " + "interpreter implementation is used. Use 'py' to force " + "implementation-agnostic wheels."), +) + + +abi = partial( + Option, + '--abi', + dest='abi', + metavar='abi', + default=None, + help=("Only use wheels compatible with Python " + "abi , e.g. 'pypy_41'. If not specified, then the " + "current interpreter abi tag is used. Generally " + "you will need to specify --implementation, " + "--platform, and --python-version when using " + "this option."), +) + + +def prefer_binary(): + return Option( + "--prefer-binary", + dest="prefer_binary", + action="store_true", + default=False, + help="Prefer older binary packages over newer source packages." + ) + + +cache_dir = partial( + Option, + "--cache-dir", + dest="cache_dir", + default=USER_CACHE_DIR, + metavar="dir", + help="Store the cache data in ." +) + +no_cache = partial( + Option, + "--no-cache-dir", + dest="cache_dir", + action="store_false", + help="Disable the cache.", +) + +no_deps = partial( + Option, + '--no-deps', '--no-dependencies', + dest='ignore_dependencies', + action='store_true', + default=False, + help="Don't install package dependencies.", +) # type: Any + +build_dir = partial( + Option, + '-b', '--build', '--build-dir', '--build-directory', + dest='build_dir', + metavar='dir', + help='Directory to unpack packages into and build in. Note that ' + 'an initial build still takes place in a temporary directory. ' + 'The location of temporary directories can be controlled by setting ' + 'the TMPDIR environment variable (TEMP on Windows) appropriately. ' + 'When passed, build directories are not cleaned in case of failures.' +) # type: Any + +ignore_requires_python = partial( + Option, + '--ignore-requires-python', + dest='ignore_requires_python', + action='store_true', + help='Ignore the Requires-Python information.' +) # type: Any + +no_build_isolation = partial( + Option, + '--no-build-isolation', + dest='build_isolation', + action='store_false', + default=True, + help='Disable isolation when building a modern source distribution. ' + 'Build dependencies specified by PEP 518 must be already installed ' + 'if this option is used.' +) # type: Any + +install_options = partial( + Option, + '--install-option', + dest='install_options', + action='append', + metavar='options', + help="Extra arguments to be supplied to the setup.py install " + "command (use like --install-option=\"--install-scripts=/usr/local/" + "bin\"). Use multiple --install-option options to pass multiple " + "options to setup.py install. If you are using an option with a " + "directory path, be sure to use absolute path.", +) # type: Any + +global_options = partial( + Option, + '--global-option', + dest='global_options', + action='append', + metavar='options', + help="Extra global options to be supplied to the setup.py " + "call before the install command.", +) # type: Any + +no_clean = partial( + Option, + '--no-clean', + action='store_true', + default=False, + help="Don't clean up build directories." +) # type: Any + +pre = partial( + Option, + '--pre', + action='store_true', + default=False, + help="Include pre-release and development versions. By default, " + "pip only finds stable versions.", +) # type: Any + +disable_pip_version_check = partial( + Option, + "--disable-pip-version-check", + dest="disable_pip_version_check", + action="store_true", + default=True, + help="Don't periodically check PyPI to determine whether a new version " + "of pip is available for download. Implied with --no-index.", +) # type: Any + + +# Deprecated, Remove later +always_unzip = partial( + Option, + '-Z', '--always-unzip', + dest='always_unzip', + action='store_true', + help=SUPPRESS_HELP, +) # type: Any + + +def _merge_hash(option, opt_str, value, parser): + """Given a value spelled "algo:digest", append the digest to a list + pointed to in a dict by the algo name.""" + if not parser.values.hashes: + parser.values.hashes = {} + try: + algo, digest = value.split(':', 1) + except ValueError: + parser.error('Arguments to %s must be a hash name ' + 'followed by a value, like --hash=sha256:abcde...' % + opt_str) + if algo not in STRONG_HASHES: + parser.error('Allowed hash algorithms for %s are %s.' % + (opt_str, ', '.join(STRONG_HASHES))) + parser.values.hashes.setdefault(algo, []).append(digest) + + +hash = partial( + Option, + '--hash', + # Hash values eventually end up in InstallRequirement.hashes due to + # __dict__ copying in process_line(). + dest='hashes', + action='callback', + callback=_merge_hash, + type='string', + help="Verify that the package's archive matches this " + 'hash before installing. Example: --hash=sha256:abcdef...', +) # type: Any + + +require_hashes = partial( + Option, + '--require-hashes', + dest='require_hashes', + action='store_true', + default=False, + help='Require a hash to check each requirement against, for ' + 'repeatable installs. This option is implied when any package in a ' + 'requirements file has a --hash option.', +) # type: Any + + +########## +# groups # +########## + +general_group = { + 'name': 'General Options', + 'options': [ + help_, + isolated_mode, + require_virtualenv, + verbose, + version, + quiet, + log, + no_input, + proxy, + retries, + timeout, + skip_requirements_regex, + exists_action, + trusted_host, + cert, + client_cert, + cache_dir, + no_cache, + disable_pip_version_check, + no_color, + ] +} + +index_group = { + 'name': 'Package Index Options', + 'options': [ + index_url, + extra_index_url, + no_index, + find_links, + process_dependency_links, + ] +} diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/main_parser.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/main_parser.py new file mode 100644 index 0000000..1774a6b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/main_parser.py @@ -0,0 +1,96 @@ +"""A single place for constructing and exposing the main parser +""" + +import os +import sys + +from pip import __version__ +from pip._internal.cli import cmdoptions +from pip._internal.cli.parser import ( + ConfigOptionParser, UpdatingDefaultsHelpFormatter, +) +from pip._internal.commands import ( + commands_dict, get_similar_commands, get_summaries, +) +from pip._internal.exceptions import CommandError +from pip._internal.utils.misc import get_prog + +__all__ = ["create_main_parser", "parse_command"] + + +def create_main_parser(): + """Creates and returns the main parser for pip's CLI + """ + + parser_kw = { + 'usage': '\n%prog [options]', + 'add_help_option': False, + 'formatter': UpdatingDefaultsHelpFormatter(), + 'name': 'global', + 'prog': get_prog(), + } + + parser = ConfigOptionParser(**parser_kw) + parser.disable_interspersed_args() + + pip_pkg_dir = os.path.abspath(os.path.join( + os.path.dirname(__file__), "..", "..", + )) + parser.version = 'pip %s from %s (python %s)' % ( + __version__, pip_pkg_dir, sys.version[:3], + ) + + # add the general options + gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, parser) + parser.add_option_group(gen_opts) + + parser.main = True # so the help formatter knows + + # create command listing for description + command_summaries = get_summaries() + description = [''] + ['%-27s %s' % (i, j) for i, j in command_summaries] + parser.description = '\n'.join(description) + + return parser + + +def parse_command(args): + parser = create_main_parser() + + # Note: parser calls disable_interspersed_args(), so the result of this + # call is to split the initial args into the general options before the + # subcommand and everything else. + # For example: + # args: ['--timeout=5', 'install', '--user', 'INITools'] + # general_options: ['--timeout==5'] + # args_else: ['install', '--user', 'INITools'] + general_options, args_else = parser.parse_args(args) + + # --version + if general_options.version: + sys.stdout.write(parser.version) + sys.stdout.write(os.linesep) + sys.exit() + + # pip || pip help -> print_help() + if not args_else or (args_else[0] == 'help' and len(args_else) == 1): + parser.print_help() + sys.exit() + + # the subcommand name + cmd_name = args_else[0] + + if cmd_name not in commands_dict: + guess = get_similar_commands(cmd_name) + + msg = ['unknown command "%s"' % cmd_name] + if guess: + msg.append('maybe you meant "%s"' % guess) + + raise CommandError(' - '.join(msg)) + + # all the args without the subcommand + cmd_args = args[:] + cmd_args.remove(cmd_name) + + return cmd_name, cmd_args diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/parser.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/parser.py new file mode 100644 index 0000000..e1eaac4 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/parser.py @@ -0,0 +1,261 @@ +"""Base option parser setup""" +from __future__ import absolute_import + +import logging +import optparse +import sys +import textwrap +from distutils.util import strtobool + +from pip._vendor.six import string_types + +from pip._internal.cli.status_codes import UNKNOWN_ERROR +from pip._internal.configuration import Configuration, ConfigurationError +from pip._internal.utils.compat import get_terminal_size + +logger = logging.getLogger(__name__) + + +class PrettyHelpFormatter(optparse.IndentedHelpFormatter): + """A prettier/less verbose help formatter for optparse.""" + + def __init__(self, *args, **kwargs): + # help position must be aligned with __init__.parseopts.description + kwargs['max_help_position'] = 30 + kwargs['indent_increment'] = 1 + kwargs['width'] = get_terminal_size()[0] - 2 + optparse.IndentedHelpFormatter.__init__(self, *args, **kwargs) + + def format_option_strings(self, option): + return self._format_option_strings(option, ' <%s>', ', ') + + def _format_option_strings(self, option, mvarfmt=' <%s>', optsep=', '): + """ + Return a comma-separated list of option strings and metavars. + + :param option: tuple of (short opt, long opt), e.g: ('-f', '--format') + :param mvarfmt: metavar format string - evaluated as mvarfmt % metavar + :param optsep: separator + """ + opts = [] + + if option._short_opts: + opts.append(option._short_opts[0]) + if option._long_opts: + opts.append(option._long_opts[0]) + if len(opts) > 1: + opts.insert(1, optsep) + + if option.takes_value(): + metavar = option.metavar or option.dest.lower() + opts.append(mvarfmt % metavar.lower()) + + return ''.join(opts) + + def format_heading(self, heading): + if heading == 'Options': + return '' + return heading + ':\n' + + def format_usage(self, usage): + """ + Ensure there is only one newline between usage and the first heading + if there is no description. + """ + msg = '\nUsage: %s\n' % self.indent_lines(textwrap.dedent(usage), " ") + return msg + + def format_description(self, description): + # leave full control over description to us + if description: + if hasattr(self.parser, 'main'): + label = 'Commands' + else: + label = 'Description' + # some doc strings have initial newlines, some don't + description = description.lstrip('\n') + # some doc strings have final newlines and spaces, some don't + description = description.rstrip() + # dedent, then reindent + description = self.indent_lines(textwrap.dedent(description), " ") + description = '%s:\n%s\n' % (label, description) + return description + else: + return '' + + def format_epilog(self, epilog): + # leave full control over epilog to us + if epilog: + return epilog + else: + return '' + + def indent_lines(self, text, indent): + new_lines = [indent + line for line in text.split('\n')] + return "\n".join(new_lines) + + +class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter): + """Custom help formatter for use in ConfigOptionParser. + + This is updates the defaults before expanding them, allowing + them to show up correctly in the help listing. + """ + + def expand_default(self, option): + if self.parser is not None: + self.parser._update_defaults(self.parser.defaults) + return optparse.IndentedHelpFormatter.expand_default(self, option) + + +class CustomOptionParser(optparse.OptionParser): + + def insert_option_group(self, idx, *args, **kwargs): + """Insert an OptionGroup at a given position.""" + group = self.add_option_group(*args, **kwargs) + + self.option_groups.pop() + self.option_groups.insert(idx, group) + + return group + + @property + def option_list_all(self): + """Get a list of all options, including those in option groups.""" + res = self.option_list[:] + for i in self.option_groups: + res.extend(i.option_list) + + return res + + +class ConfigOptionParser(CustomOptionParser): + """Custom option parser which updates its defaults by checking the + configuration files and environmental variables""" + + def __init__(self, *args, **kwargs): + self.name = kwargs.pop('name') + + isolated = kwargs.pop("isolated", False) + self.config = Configuration(isolated) + + assert self.name + optparse.OptionParser.__init__(self, *args, **kwargs) + + def check_default(self, option, key, val): + try: + return option.check_value(key, val) + except optparse.OptionValueError as exc: + print("An error occurred during configuration: %s" % exc) + sys.exit(3) + + def _get_ordered_configuration_items(self): + # Configuration gives keys in an unordered manner. Order them. + override_order = ["global", self.name, ":env:"] + + # Pool the options into different groups + section_items = {name: [] for name in override_order} + for section_key, val in self.config.items(): + # ignore empty values + if not val: + logger.debug( + "Ignoring configuration key '%s' as it's value is empty.", + section_key + ) + continue + + section, key = section_key.split(".", 1) + if section in override_order: + section_items[section].append((key, val)) + + # Yield each group in their override order + for section in override_order: + for key, val in section_items[section]: + yield key, val + + def _update_defaults(self, defaults): + """Updates the given defaults with values from the config files and + the environ. Does a little special handling for certain types of + options (lists).""" + + # Accumulate complex default state. + self.values = optparse.Values(self.defaults) + late_eval = set() + # Then set the options with those values + for key, val in self._get_ordered_configuration_items(): + # '--' because configuration supports only long names + option = self.get_option('--' + key) + + # Ignore options not present in this parser. E.g. non-globals put + # in [global] by users that want them to apply to all applicable + # commands. + if option is None: + continue + + if option.action in ('store_true', 'store_false', 'count'): + try: + val = strtobool(val) + except ValueError: + error_msg = invalid_config_error_message( + option.action, key, val + ) + self.error(error_msg) + + elif option.action == 'append': + val = val.split() + val = [self.check_default(option, key, v) for v in val] + elif option.action == 'callback': + late_eval.add(option.dest) + opt_str = option.get_opt_string() + val = option.convert_value(opt_str, val) + # From take_action + args = option.callback_args or () + kwargs = option.callback_kwargs or {} + option.callback(option, opt_str, val, self, *args, **kwargs) + else: + val = self.check_default(option, key, val) + + defaults[option.dest] = val + + for key in late_eval: + defaults[key] = getattr(self.values, key) + self.values = None + return defaults + + def get_default_values(self): + """Overriding to make updating the defaults after instantiation of + the option parser possible, _update_defaults() does the dirty work.""" + if not self.process_default_values: + # Old, pre-Optik 1.5 behaviour. + return optparse.Values(self.defaults) + + # Load the configuration, or error out in case of an error + try: + self.config.load() + except ConfigurationError as err: + self.exit(UNKNOWN_ERROR, str(err)) + + defaults = self._update_defaults(self.defaults.copy()) # ours + for option in self._get_all_options(): + default = defaults.get(option.dest) + if isinstance(default, string_types): + opt_str = option.get_opt_string() + defaults[option.dest] = option.check_value(opt_str, default) + return optparse.Values(defaults) + + def error(self, msg): + self.print_usage(sys.stderr) + self.exit(UNKNOWN_ERROR, "%s\n" % msg) + + +def invalid_config_error_message(action, key, val): + """Returns a better error message when invalid configuration option + is provided.""" + if action in ('store_true', 'store_false'): + return ("{0} is not a valid value for {1} option, " + "please specify a boolean value like yes/no, " + "true/false or 1/0 instead.").format(val, key) + + return ("{0} is not a valid value for {1} option, " + "please specify a numerical value like 1/0 " + "instead.").format(val, key) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/status_codes.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/status_codes.py new file mode 100644 index 0000000..275360a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/cli/status_codes.py @@ -0,0 +1,8 @@ +from __future__ import absolute_import + +SUCCESS = 0 +ERROR = 1 +UNKNOWN_ERROR = 2 +VIRTUALENV_NOT_FOUND = 3 +PREVIOUS_BUILD_DIR_ERROR = 4 +NO_MATCHES_FOUND = 23 diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/__init__.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/__init__.py new file mode 100644 index 0000000..c7d1da3 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/__init__.py @@ -0,0 +1,79 @@ +""" +Package containing all pip commands +""" +from __future__ import absolute_import + +from pip._internal.commands.completion import CompletionCommand +from pip._internal.commands.configuration import ConfigurationCommand +from pip._internal.commands.download import DownloadCommand +from pip._internal.commands.freeze import FreezeCommand +from pip._internal.commands.hash import HashCommand +from pip._internal.commands.help import HelpCommand +from pip._internal.commands.list import ListCommand +from pip._internal.commands.check import CheckCommand +from pip._internal.commands.search import SearchCommand +from pip._internal.commands.show import ShowCommand +from pip._internal.commands.install import InstallCommand +from pip._internal.commands.uninstall import UninstallCommand +from pip._internal.commands.wheel import WheelCommand + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Type # noqa: F401 + from pip._internal.cli.base_command import Command # noqa: F401 + +commands_order = [ + InstallCommand, + DownloadCommand, + UninstallCommand, + FreezeCommand, + ListCommand, + ShowCommand, + CheckCommand, + ConfigurationCommand, + SearchCommand, + WheelCommand, + HashCommand, + CompletionCommand, + HelpCommand, +] # type: List[Type[Command]] + +commands_dict = {c.name: c for c in commands_order} + + +def get_summaries(ordered=True): + """Yields sorted (command name, command summary) tuples.""" + + if ordered: + cmditems = _sort_commands(commands_dict, commands_order) + else: + cmditems = commands_dict.items() + + for name, command_class in cmditems: + yield (name, command_class.summary) + + +def get_similar_commands(name): + """Command name auto-correct.""" + from difflib import get_close_matches + + name = name.lower() + + close_commands = get_close_matches(name, commands_dict.keys()) + + if close_commands: + return close_commands[0] + else: + return False + + +def _sort_commands(cmddict, order): + def keyfn(key): + try: + return order.index(key[1]) + except ValueError: + # unordered items should come last + return 0xff + + return sorted(cmddict.items(), key=keyfn) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/check.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/check.py new file mode 100644 index 0000000..1be3ec2 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/check.py @@ -0,0 +1,41 @@ +import logging + +from pip._internal.cli.base_command import Command +from pip._internal.operations.check import ( + check_package_set, create_package_set_from_installed, +) + +logger = logging.getLogger(__name__) + + +class CheckCommand(Command): + """Verify installed packages have compatible dependencies.""" + name = 'check' + usage = """ + %prog [options]""" + summary = 'Verify installed packages have compatible dependencies.' + + def run(self, options, args): + package_set = create_package_set_from_installed() + missing, conflicting = check_package_set(package_set) + + for project_name in missing: + version = package_set[project_name].version + for dependency in missing[project_name]: + logger.info( + "%s %s requires %s, which is not installed.", + project_name, version, dependency[0], + ) + + for project_name in conflicting: + version = package_set[project_name].version + for dep_name, dep_version, req in conflicting[project_name]: + logger.info( + "%s %s has requirement %s, but you have %s %s.", + project_name, version, req, dep_name, dep_version, + ) + + if missing or conflicting: + return 1 + else: + logger.info("No broken requirements found.") diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/completion.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/completion.py new file mode 100644 index 0000000..2fcdd39 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/completion.py @@ -0,0 +1,94 @@ +from __future__ import absolute_import + +import sys +import textwrap + +from pip._internal.cli.base_command import Command +from pip._internal.utils.misc import get_prog + +BASE_COMPLETION = """ +# pip %(shell)s completion start%(script)s# pip %(shell)s completion end +""" + +COMPLETION_SCRIPTS = { + 'bash': """ + _pip_completion() + { + COMPREPLY=( $( COMP_WORDS="${COMP_WORDS[*]}" \\ + COMP_CWORD=$COMP_CWORD \\ + PIP_AUTO_COMPLETE=1 $1 ) ) + } + complete -o default -F _pip_completion %(prog)s + """, + 'zsh': """ + function _pip_completion { + local words cword + read -Ac words + read -cn cword + reply=( $( COMP_WORDS="$words[*]" \\ + COMP_CWORD=$(( cword-1 )) \\ + PIP_AUTO_COMPLETE=1 $words[1] ) ) + } + compctl -K _pip_completion %(prog)s + """, + 'fish': """ + function __fish_complete_pip + set -lx COMP_WORDS (commandline -o) "" + set -lx COMP_CWORD ( \\ + math (contains -i -- (commandline -t) $COMP_WORDS)-1 \\ + ) + set -lx PIP_AUTO_COMPLETE 1 + string split \\ -- (eval $COMP_WORDS[1]) + end + complete -fa "(__fish_complete_pip)" -c %(prog)s + """, +} + + +class CompletionCommand(Command): + """A helper command to be used for command completion.""" + name = 'completion' + summary = 'A helper command used for command completion.' + ignore_require_venv = True + + def __init__(self, *args, **kw): + super(CompletionCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option( + '--bash', '-b', + action='store_const', + const='bash', + dest='shell', + help='Emit completion code for bash') + cmd_opts.add_option( + '--zsh', '-z', + action='store_const', + const='zsh', + dest='shell', + help='Emit completion code for zsh') + cmd_opts.add_option( + '--fish', '-f', + action='store_const', + const='fish', + dest='shell', + help='Emit completion code for fish') + + self.parser.insert_option_group(0, cmd_opts) + + def run(self, options, args): + """Prints the completion code of the given shell""" + shells = COMPLETION_SCRIPTS.keys() + shell_options = ['--' + shell for shell in sorted(shells)] + if options.shell in shells: + script = textwrap.dedent( + COMPLETION_SCRIPTS.get(options.shell, '') % { + 'prog': get_prog(), + } + ) + print(BASE_COMPLETION % {'script': script, 'shell': options.shell}) + else: + sys.stderr.write( + 'ERROR: You must pass %s\n' % ' or '.join(shell_options) + ) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/configuration.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/configuration.py new file mode 100644 index 0000000..826c08d --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/configuration.py @@ -0,0 +1,227 @@ +import logging +import os +import subprocess + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.configuration import Configuration, kinds +from pip._internal.exceptions import PipError +from pip._internal.locations import venv_config_file +from pip._internal.utils.misc import get_prog + +logger = logging.getLogger(__name__) + + +class ConfigurationCommand(Command): + """Manage local and global configuration. + + Subcommands: + + list: List the active configuration (or from the file specified) + edit: Edit the configuration file in an editor + get: Get the value associated with name + set: Set the name=value + unset: Unset the value associated with name + + If none of --user, --global and --venv are passed, a virtual + environment configuration file is used if one is active and the file + exists. Otherwise, all modifications happen on the to the user file by + default. + """ + + name = 'config' + usage = """ + %prog [] list + %prog [] [--editor ] edit + + %prog [] get name + %prog [] set name value + %prog [] unset name + """ + + summary = "Manage local and global configuration." + + def __init__(self, *args, **kwargs): + super(ConfigurationCommand, self).__init__(*args, **kwargs) + + self.configuration = None + + self.cmd_opts.add_option( + '--editor', + dest='editor', + action='store', + default=None, + help=( + 'Editor to use to edit the file. Uses VISUAL or EDITOR ' + 'environment variables if not provided.' + ) + ) + + self.cmd_opts.add_option( + '--global', + dest='global_file', + action='store_true', + default=False, + help='Use the system-wide configuration file only' + ) + + self.cmd_opts.add_option( + '--user', + dest='user_file', + action='store_true', + default=False, + help='Use the user configuration file only' + ) + + self.cmd_opts.add_option( + '--venv', + dest='venv_file', + action='store_true', + default=False, + help='Use the virtualenv configuration file only' + ) + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + handlers = { + "list": self.list_values, + "edit": self.open_in_editor, + "get": self.get_name, + "set": self.set_name_value, + "unset": self.unset_name + } + + # Determine action + if not args or args[0] not in handlers: + logger.error("Need an action ({}) to perform.".format( + ", ".join(sorted(handlers))) + ) + return ERROR + + action = args[0] + + # Determine which configuration files are to be loaded + # Depends on whether the command is modifying. + try: + load_only = self._determine_file( + options, need_value=(action in ["get", "set", "unset", "edit"]) + ) + except PipError as e: + logger.error(e.args[0]) + return ERROR + + # Load a new configuration + self.configuration = Configuration( + isolated=options.isolated_mode, load_only=load_only + ) + self.configuration.load() + + # Error handling happens here, not in the action-handlers. + try: + handlers[action](options, args[1:]) + except PipError as e: + logger.error(e.args[0]) + return ERROR + + return SUCCESS + + def _determine_file(self, options, need_value): + file_options = { + kinds.USER: options.user_file, + kinds.GLOBAL: options.global_file, + kinds.VENV: options.venv_file + } + + if sum(file_options.values()) == 0: + if not need_value: + return None + # Default to user, unless there's a virtualenv file. + elif os.path.exists(venv_config_file): + return kinds.VENV + else: + return kinds.USER + elif sum(file_options.values()) == 1: + # There's probably a better expression for this. + return [key for key in file_options if file_options[key]][0] + + raise PipError( + "Need exactly one file to operate upon " + "(--user, --venv, --global) to perform." + ) + + def list_values(self, options, args): + self._get_n_args(args, "list", n=0) + + for key, value in sorted(self.configuration.items()): + logger.info("%s=%r", key, value) + + def get_name(self, options, args): + key = self._get_n_args(args, "get [name]", n=1) + value = self.configuration.get_value(key) + + logger.info("%s", value) + + def set_name_value(self, options, args): + key, value = self._get_n_args(args, "set [name] [value]", n=2) + self.configuration.set_value(key, value) + + self._save_configuration() + + def unset_name(self, options, args): + key = self._get_n_args(args, "unset [name]", n=1) + self.configuration.unset_value(key) + + self._save_configuration() + + def open_in_editor(self, options, args): + editor = self._determine_editor(options) + + fname = self.configuration.get_file_to_edit() + if fname is None: + raise PipError("Could not determine appropriate file.") + + try: + subprocess.check_call([editor, fname]) + except subprocess.CalledProcessError as e: + raise PipError( + "Editor Subprocess exited with exit code {}" + .format(e.returncode) + ) + + def _get_n_args(self, args, example, n): + """Helper to make sure the command got the right number of arguments + """ + if len(args) != n: + msg = ( + 'Got unexpected number of arguments, expected {}. ' + '(example: "{} config {}")' + ).format(n, get_prog(), example) + raise PipError(msg) + + if n == 1: + return args[0] + else: + return args + + def _save_configuration(self): + # We successfully ran a modifying command. Need to save the + # configuration. + try: + self.configuration.save() + except Exception: + logger.error( + "Unable to save configuration. Please report this as a bug.", + exc_info=1 + ) + raise PipError("Internal Error.") + + def _determine_editor(self, options): + if options.editor is not None: + return options.editor + elif "VISUAL" in os.environ: + return os.environ["VISUAL"] + elif "EDITOR" in os.environ: + return os.environ["EDITOR"] + else: + raise PipError("Could not determine editor to use.") diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/download.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/download.py new file mode 100644 index 0000000..b3f3c6e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/download.py @@ -0,0 +1,174 @@ +from __future__ import absolute_import + +import logging +import os + +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import RequirementCommand +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req import RequirementSet +from pip._internal.req.req_tracker import RequirementTracker +from pip._internal.resolve import Resolver +from pip._internal.utils.filesystem import check_path_owner +from pip._internal.utils.misc import ensure_dir, normalize_path +from pip._internal.utils.temp_dir import TempDirectory + +logger = logging.getLogger(__name__) + + +class DownloadCommand(RequirementCommand): + """ + Download packages from: + + - PyPI (and other indexes) using requirement specifiers. + - VCS project urls. + - Local project directories. + - Local or remote source archives. + + pip also supports downloading from "requirements files", which provide + an easy way to specify a whole environment to be downloaded. + """ + name = 'download' + + usage = """ + %prog [options] [package-index-options] ... + %prog [options] -r [package-index-options] ... + %prog [options] ... + %prog [options] ... + %prog [options] ...""" + + summary = 'Download packages.' + + def __init__(self, *args, **kw): + super(DownloadCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option(cmdoptions.constraints()) + cmd_opts.add_option(cmdoptions.requirements()) + cmd_opts.add_option(cmdoptions.build_dir()) + cmd_opts.add_option(cmdoptions.no_deps()) + cmd_opts.add_option(cmdoptions.global_options()) + cmd_opts.add_option(cmdoptions.no_binary()) + cmd_opts.add_option(cmdoptions.only_binary()) + cmd_opts.add_option(cmdoptions.prefer_binary()) + cmd_opts.add_option(cmdoptions.src()) + cmd_opts.add_option(cmdoptions.pre()) + cmd_opts.add_option(cmdoptions.no_clean()) + cmd_opts.add_option(cmdoptions.require_hashes()) + cmd_opts.add_option(cmdoptions.progress_bar()) + cmd_opts.add_option(cmdoptions.no_build_isolation()) + + cmd_opts.add_option( + '-d', '--dest', '--destination-dir', '--destination-directory', + dest='download_dir', + metavar='dir', + default=os.curdir, + help=("Download packages into ."), + ) + + cmd_opts.add_option(cmdoptions.platform()) + cmd_opts.add_option(cmdoptions.python_version()) + cmd_opts.add_option(cmdoptions.implementation()) + cmd_opts.add_option(cmdoptions.abi()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, cmd_opts) + + def run(self, options, args): + options.ignore_installed = True + # editable doesn't really make sense for `pip download`, but the bowels + # of the RequirementSet code require that property. + options.editables = [] + + if options.python_version: + python_versions = [options.python_version] + else: + python_versions = None + + cmdoptions.check_dist_restriction(options) + + options.src_dir = os.path.abspath(options.src_dir) + options.download_dir = normalize_path(options.download_dir) + + ensure_dir(options.download_dir) + + with self._build_session(options) as session: + finder = self._build_package_finder( + options=options, + session=session, + platform=options.platform, + python_versions=python_versions, + abi=options.abi, + implementation=options.implementation, + ) + build_delete = (not (options.no_clean or options.build_dir)) + if options.cache_dir and not check_path_owner(options.cache_dir): + logger.warning( + "The directory '%s' or its parent directory is not owned " + "by the current user and caching wheels has been " + "disabled. check the permissions and owner of that " + "directory. If executing pip with sudo, you may want " + "sudo's -H flag.", + options.cache_dir, + ) + options.cache_dir = None + + with RequirementTracker() as req_tracker, TempDirectory( + options.build_dir, delete=build_delete, kind="download" + ) as directory: + + requirement_set = RequirementSet( + require_hashes=options.require_hashes, + ) + self.populate_requirement_set( + requirement_set, + args, + options, + finder, + session, + self.name, + None + ) + + preparer = RequirementPreparer( + build_dir=directory.path, + src_dir=options.src_dir, + download_dir=options.download_dir, + wheel_download_dir=None, + progress_bar=options.progress_bar, + build_isolation=options.build_isolation, + req_tracker=req_tracker, + ) + + resolver = Resolver( + preparer=preparer, + finder=finder, + session=session, + wheel_cache=None, + use_user_site=False, + upgrade_strategy="to-satisfy-only", + force_reinstall=False, + ignore_dependencies=options.ignore_dependencies, + ignore_requires_python=False, + ignore_installed=True, + isolated=options.isolated_mode, + ) + resolver.resolve(requirement_set) + + downloaded = ' '.join([ + req.name for req in requirement_set.successfully_downloaded + ]) + if downloaded: + logger.info('Successfully downloaded %s', downloaded) + + # Clean up + if not options.no_clean: + requirement_set.cleanup_files() + + return requirement_set diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/freeze.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/freeze.py new file mode 100644 index 0000000..dc9c53a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/freeze.py @@ -0,0 +1,96 @@ +from __future__ import absolute_import + +import sys + +from pip._internal.cache import WheelCache +from pip._internal.cli.base_command import Command +from pip._internal.models.format_control import FormatControl +from pip._internal.operations.freeze import freeze +from pip._internal.utils.compat import stdlib_pkgs + +DEV_PKGS = {'pip', 'setuptools', 'distribute', 'wheel'} + + +class FreezeCommand(Command): + """ + Output installed packages in requirements format. + + packages are listed in a case-insensitive sorted order. + """ + name = 'freeze' + usage = """ + %prog [options]""" + summary = 'Output installed packages in requirements format.' + log_streams = ("ext://sys.stderr", "ext://sys.stderr") + + def __init__(self, *args, **kw): + super(FreezeCommand, self).__init__(*args, **kw) + + self.cmd_opts.add_option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help="Use the order in the given requirements file and its " + "comments when generating output. This option can be " + "used multiple times.") + self.cmd_opts.add_option( + '-f', '--find-links', + dest='find_links', + action='append', + default=[], + metavar='URL', + help='URL for finding packages, which will be added to the ' + 'output.') + self.cmd_opts.add_option( + '-l', '--local', + dest='local', + action='store_true', + default=False, + help='If in a virtualenv that has global access, do not output ' + 'globally-installed packages.') + self.cmd_opts.add_option( + '--user', + dest='user', + action='store_true', + default=False, + help='Only output packages installed in user-site.') + self.cmd_opts.add_option( + '--all', + dest='freeze_all', + action='store_true', + help='Do not skip these packages in the output:' + ' %s' % ', '.join(DEV_PKGS)) + self.cmd_opts.add_option( + '--exclude-editable', + dest='exclude_editable', + action='store_true', + help='Exclude editable package from output.') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + format_control = FormatControl(set(), set()) + wheel_cache = WheelCache(options.cache_dir, format_control) + skip = set(stdlib_pkgs) + if not options.freeze_all: + skip.update(DEV_PKGS) + + freeze_kwargs = dict( + requirement=options.requirements, + find_links=options.find_links, + local_only=options.local, + user_only=options.user, + skip_regex=options.skip_requirements_regex, + isolated=options.isolated_mode, + wheel_cache=wheel_cache, + skip=skip, + exclude_editable=options.exclude_editable, + ) + + try: + for line in freeze(**freeze_kwargs): + sys.stdout.write(line + '\n') + finally: + wheel_cache.cleanup() diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/hash.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/hash.py new file mode 100644 index 0000000..423440e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/hash.py @@ -0,0 +1,57 @@ +from __future__ import absolute_import + +import hashlib +import logging +import sys + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR +from pip._internal.utils.hashes import FAVORITE_HASH, STRONG_HASHES +from pip._internal.utils.misc import read_chunks + +logger = logging.getLogger(__name__) + + +class HashCommand(Command): + """ + Compute a hash of a local package archive. + + These can be used with --hash in a requirements file to do repeatable + installs. + + """ + name = 'hash' + usage = '%prog [options] ...' + summary = 'Compute hashes of package archives.' + ignore_require_venv = True + + def __init__(self, *args, **kw): + super(HashCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '-a', '--algorithm', + dest='algorithm', + choices=STRONG_HASHES, + action='store', + default=FAVORITE_HASH, + help='The hash algorithm to use: one of %s' % + ', '.join(STRONG_HASHES)) + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + if not args: + self.parser.print_usage(sys.stderr) + return ERROR + + algorithm = options.algorithm + for path in args: + logger.info('%s:\n--hash=%s:%s', + path, algorithm, _hash_of_file(path, algorithm)) + + +def _hash_of_file(path, algorithm): + """Return the hash digest of a file.""" + with open(path, 'rb') as archive: + hash = hashlib.new(algorithm) + for chunk in read_chunks(archive): + hash.update(chunk) + return hash.hexdigest() diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/help.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/help.py new file mode 100644 index 0000000..49a81cb --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/help.py @@ -0,0 +1,37 @@ +from __future__ import absolute_import + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.exceptions import CommandError + + +class HelpCommand(Command): + """Show help for commands""" + name = 'help' + usage = """ + %prog """ + summary = 'Show help for commands.' + ignore_require_venv = True + + def run(self, options, args): + from pip._internal.commands import commands_dict, get_similar_commands + + try: + # 'pip help' with no args is handled by pip.__init__.parseopt() + cmd_name = args[0] # the command we need help for + except IndexError: + return SUCCESS + + if cmd_name not in commands_dict: + guess = get_similar_commands(cmd_name) + + msg = ['unknown command "%s"' % cmd_name] + if guess: + msg.append('maybe you meant "%s"' % guess) + + raise CommandError(' - '.join(msg)) + + command = commands_dict[cmd_name]() + command.parser.print_help() + + return SUCCESS diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/install.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/install.py new file mode 100644 index 0000000..c9ed3b4 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/install.py @@ -0,0 +1,555 @@ +from __future__ import absolute_import + +import errno +import logging +import operator +import os +import shutil +from optparse import SUPPRESS_HELP + +from pip._vendor import pkg_resources + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import RequirementCommand +from pip._internal.cli.status_codes import ERROR +from pip._internal.exceptions import ( + CommandError, InstallationError, PreviousBuildDirError, +) +from pip._internal.locations import distutils_scheme, virtualenv_no_global +from pip._internal.operations.check import check_install_conflicts +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req import RequirementSet, install_given_reqs +from pip._internal.req.req_tracker import RequirementTracker +from pip._internal.resolve import Resolver +from pip._internal.utils.filesystem import check_path_owner +from pip._internal.utils.misc import ( + ensure_dir, get_installed_version, + protect_pip_from_modification_on_windows, +) +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.wheel import WheelBuilder + +try: + import wheel +except ImportError: + wheel = None + +from pip._internal.locations import running_under_virtualenv + +logger = logging.getLogger(__name__) + + +class InstallCommand(RequirementCommand): + """ + Install packages from: + + - PyPI (and other indexes) using requirement specifiers. + - VCS project urls. + - Local project directories. + - Local or remote source archives. + + pip also supports installing from "requirements files", which provide + an easy way to specify a whole environment to be installed. + """ + name = 'install' + + usage = """ + %prog [options] [package-index-options] ... + %prog [options] -r [package-index-options] ... + %prog [options] [-e] ... + %prog [options] [-e] ... + %prog [options] ...""" + + summary = 'Install packages.' + + def __init__(self, *args, **kw): + super(InstallCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option(cmdoptions.requirements()) + cmd_opts.add_option(cmdoptions.constraints()) + cmd_opts.add_option(cmdoptions.no_deps()) + cmd_opts.add_option(cmdoptions.pre()) + + cmd_opts.add_option(cmdoptions.editable()) + cmd_opts.add_option( + '-t', '--target', + dest='target_dir', + metavar='dir', + default=None, + help='Install packages into . ' + 'By default this will not replace existing files/folders in ' + '. Use --upgrade to replace existing packages in ' + 'with new versions.' + ) + cmd_opts.add_option(cmdoptions.platform()) + cmd_opts.add_option(cmdoptions.python_version()) + cmd_opts.add_option(cmdoptions.implementation()) + cmd_opts.add_option(cmdoptions.abi()) + + cmd_opts.add_option( + '--user', + dest='use_user_site', + action='store_true', + help="Install to the Python user install directory for your " + "platform. Typically ~/.local/, or %APPDATA%\\Python on " + "Windows. (See the Python documentation for site.USER_BASE " + "for full details.) On Debian systems, this is the " + "default when running outside of a virtual environment " + "and not as root.") + + cmd_opts.add_option( + '--no-user', + dest='use_system_location', + action='store_true', + help=SUPPRESS_HELP) + cmd_opts.add_option( + '--root', + dest='root_path', + metavar='dir', + default=None, + help="Install everything relative to this alternate root " + "directory.") + cmd_opts.add_option( + '--prefix', + dest='prefix_path', + metavar='dir', + default=None, + help="Installation prefix where lib, bin and other top-level " + "folders are placed") + + cmd_opts.add_option( + '--system', + dest='use_system_location', + action='store_true', + help="Install using the system scheme (overrides --user on " + "Debian systems)") + + cmd_opts.add_option(cmdoptions.build_dir()) + + cmd_opts.add_option(cmdoptions.src()) + + cmd_opts.add_option( + '-U', '--upgrade', + dest='upgrade', + action='store_true', + help='Upgrade all specified packages to the newest available ' + 'version. The handling of dependencies depends on the ' + 'upgrade-strategy used.' + ) + + cmd_opts.add_option( + '--upgrade-strategy', + dest='upgrade_strategy', + default='only-if-needed', + choices=['only-if-needed', 'eager'], + help='Determines how dependency upgrading should be handled ' + '[default: %default]. ' + '"eager" - dependencies are upgraded regardless of ' + 'whether the currently installed version satisfies the ' + 'requirements of the upgraded package(s). ' + '"only-if-needed" - are upgraded only when they do not ' + 'satisfy the requirements of the upgraded package(s).' + ) + + cmd_opts.add_option( + '--force-reinstall', + dest='force_reinstall', + action='store_true', + help='Reinstall all packages even if they are already ' + 'up-to-date.') + + cmd_opts.add_option( + '-I', '--ignore-installed', + dest='ignore_installed', + action='store_true', + help='Ignore the installed packages (reinstalling instead).') + + cmd_opts.add_option(cmdoptions.ignore_requires_python()) + cmd_opts.add_option(cmdoptions.no_build_isolation()) + + cmd_opts.add_option(cmdoptions.install_options()) + cmd_opts.add_option(cmdoptions.global_options()) + + cmd_opts.add_option( + "--compile", + action="store_true", + dest="compile", + default=True, + help="Compile Python source files to bytecode", + ) + + cmd_opts.add_option( + "--no-compile", + action="store_false", + dest="compile", + help="Do not compile Python source files to bytecode", + ) + + cmd_opts.add_option( + "--no-warn-script-location", + action="store_false", + dest="warn_script_location", + default=True, + help="Do not warn when installing scripts outside PATH", + ) + cmd_opts.add_option( + "--no-warn-conflicts", + action="store_false", + dest="warn_about_conflicts", + default=True, + help="Do not warn about broken dependencies", + ) + + cmd_opts.add_option(cmdoptions.no_binary()) + cmd_opts.add_option(cmdoptions.only_binary()) + cmd_opts.add_option(cmdoptions.prefer_binary()) + cmd_opts.add_option(cmdoptions.no_clean()) + cmd_opts.add_option(cmdoptions.require_hashes()) + cmd_opts.add_option(cmdoptions.progress_bar()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, cmd_opts) + + def run(self, options, args): + cmdoptions.check_install_build_global(options) + upgrade_strategy = "to-satisfy-only" + if options.upgrade: + upgrade_strategy = options.upgrade_strategy + + if options.build_dir: + options.build_dir = os.path.abspath(options.build_dir) + + cmdoptions.check_dist_restriction(options, check_target=True) + + if options.python_version: + python_versions = [options.python_version] + else: + python_versions = None + + # compute install location defaults + if (not options.use_user_site and not options.prefix_path and not + options.target_dir and not options.use_system_location): + if not running_under_virtualenv() and os.geteuid() != 0: + options.use_user_site = True + + if options.use_system_location: + options.use_user_site = False + + options.src_dir = os.path.abspath(options.src_dir) + install_options = options.install_options or [] + if options.use_user_site: + if options.prefix_path: + raise CommandError( + "Can not combine '--user' and '--prefix' as they imply " + "different installation locations" + ) + if virtualenv_no_global(): + raise InstallationError( + "Can not perform a '--user' install. User site-packages " + "are not visible in this virtualenv." + ) + install_options.append('--user') + install_options.append('--prefix=') + + target_temp_dir = TempDirectory(kind="target") + if options.target_dir: + options.ignore_installed = True + options.target_dir = os.path.abspath(options.target_dir) + if (os.path.exists(options.target_dir) and not + os.path.isdir(options.target_dir)): + raise CommandError( + "Target path exists but is not a directory, will not " + "continue." + ) + + # Create a target directory for using with the target option + target_temp_dir.create() + install_options.append('--home=' + target_temp_dir.path) + + global_options = options.global_options or [] + + with self._build_session(options) as session: + finder = self._build_package_finder( + options=options, + session=session, + platform=options.platform, + python_versions=python_versions, + abi=options.abi, + implementation=options.implementation, + ) + build_delete = (not (options.no_clean or options.build_dir)) + wheel_cache = WheelCache(options.cache_dir, options.format_control) + + if options.cache_dir and not check_path_owner(options.cache_dir): + logger.warning( + "The directory '%s' or its parent directory is not owned " + "by the current user and caching wheels has been " + "disabled. check the permissions and owner of that " + "directory. If executing pip with sudo, you may want " + "sudo's -H flag.", + options.cache_dir, + ) + options.cache_dir = None + + with RequirementTracker() as req_tracker, TempDirectory( + options.build_dir, delete=build_delete, kind="install" + ) as directory: + requirement_set = RequirementSet( + require_hashes=options.require_hashes, + check_supported_wheels=not options.target_dir, + ) + + try: + self.populate_requirement_set( + requirement_set, args, options, finder, session, + self.name, wheel_cache + ) + preparer = RequirementPreparer( + build_dir=directory.path, + src_dir=options.src_dir, + download_dir=None, + wheel_download_dir=None, + progress_bar=options.progress_bar, + build_isolation=options.build_isolation, + req_tracker=req_tracker, + ) + + resolver = Resolver( + preparer=preparer, + finder=finder, + session=session, + wheel_cache=wheel_cache, + use_user_site=options.use_user_site, + upgrade_strategy=upgrade_strategy, + force_reinstall=options.force_reinstall, + ignore_dependencies=options.ignore_dependencies, + ignore_requires_python=options.ignore_requires_python, + ignore_installed=options.ignore_installed, + isolated=options.isolated_mode, + ) + resolver.resolve(requirement_set) + + protect_pip_from_modification_on_windows( + modifying_pip=requirement_set.has_requirement("pip") + ) + + # If caching is disabled or wheel is not installed don't + # try to build wheels. + if wheel and options.cache_dir: + # build wheels before install. + wb = WheelBuilder( + finder, preparer, wheel_cache, + build_options=[], global_options=[], + ) + # Ignore the result: a failed wheel will be + # installed from the sdist/vcs whatever. + wb.build( + requirement_set.requirements.values(), + session=session, autobuilding=True + ) + + to_install = resolver.get_installation_order( + requirement_set + ) + + # Consistency Checking of the package set we're installing. + should_warn_about_conflicts = ( + not options.ignore_dependencies and + options.warn_about_conflicts + ) + if should_warn_about_conflicts: + self._warn_about_conflicts(to_install) + + # Don't warn about script install locations if + # --target has been specified + warn_script_location = options.warn_script_location + if options.target_dir: + warn_script_location = False + + installed = install_given_reqs( + to_install, + install_options, + global_options, + root=options.root_path, + home=target_temp_dir.path, + prefix=options.prefix_path, + pycompile=options.compile, + warn_script_location=warn_script_location, + use_user_site=options.use_user_site, + ) + + lib_locations = get_lib_location_guesses( + user=options.use_user_site, + home=target_temp_dir.path, + root=options.root_path, + prefix=options.prefix_path, + isolated=options.isolated_mode, + ) + working_set = pkg_resources.WorkingSet(lib_locations) + + reqs = sorted(installed, key=operator.attrgetter('name')) + items = [] + for req in reqs: + item = req.name + try: + installed_version = get_installed_version( + req.name, working_set=working_set + ) + if installed_version: + item += '-' + installed_version + except Exception: + pass + items.append(item) + installed = ' '.join(items) + if installed: + logger.info('Successfully installed %s', installed) + except EnvironmentError as error: + show_traceback = (self.verbosity >= 1) + + message = create_env_error_message( + error, show_traceback, options.use_user_site, + ) + logger.error(message, exc_info=show_traceback) + + return ERROR + except PreviousBuildDirError: + options.no_clean = True + raise + finally: + # Clean up + if not options.no_clean: + requirement_set.cleanup_files() + wheel_cache.cleanup() + + if options.target_dir: + self._handle_target_dir( + options.target_dir, target_temp_dir, options.upgrade + ) + return requirement_set + + def _handle_target_dir(self, target_dir, target_temp_dir, upgrade): + ensure_dir(target_dir) + + # Checking both purelib and platlib directories for installed + # packages to be moved to target directory + lib_dir_list = [] + + with target_temp_dir: + # Checking both purelib and platlib directories for installed + # packages to be moved to target directory + scheme = distutils_scheme('', home=target_temp_dir.path) + purelib_dir = scheme['purelib'] + platlib_dir = scheme['platlib'] + data_dir = scheme['data'] + + if os.path.exists(purelib_dir): + lib_dir_list.append(purelib_dir) + if os.path.exists(platlib_dir) and platlib_dir != purelib_dir: + lib_dir_list.append(platlib_dir) + if os.path.exists(data_dir): + lib_dir_list.append(data_dir) + + for lib_dir in lib_dir_list: + for item in os.listdir(lib_dir): + if lib_dir == data_dir: + ddir = os.path.join(data_dir, item) + if any(s.startswith(ddir) for s in lib_dir_list[:-1]): + continue + target_item_dir = os.path.join(target_dir, item) + if os.path.exists(target_item_dir): + if not upgrade: + logger.warning( + 'Target directory %s already exists. Specify ' + '--upgrade to force replacement.', + target_item_dir + ) + continue + if os.path.islink(target_item_dir): + logger.warning( + 'Target directory %s already exists and is ' + 'a link. Pip will not automatically replace ' + 'links, please remove if replacement is ' + 'desired.', + target_item_dir + ) + continue + if os.path.isdir(target_item_dir): + shutil.rmtree(target_item_dir) + else: + os.remove(target_item_dir) + + shutil.move( + os.path.join(lib_dir, item), + target_item_dir + ) + + def _warn_about_conflicts(self, to_install): + package_set, _dep_info = check_install_conflicts(to_install) + missing, conflicting = _dep_info + + # NOTE: There is some duplication here from pip check + for project_name in missing: + version = package_set[project_name][0] + for dependency in missing[project_name]: + logger.critical( + "%s %s requires %s, which is not installed.", + project_name, version, dependency[1], + ) + + for project_name in conflicting: + version = package_set[project_name][0] + for dep_name, dep_version, req in conflicting[project_name]: + logger.critical( + "%s %s has requirement %s, but you'll have %s %s which is " + "incompatible.", + project_name, version, req, dep_name, dep_version, + ) + + +def get_lib_location_guesses(*args, **kwargs): + scheme = distutils_scheme('', *args, **kwargs) + return [scheme['purelib'], scheme['platlib']] + + +def create_env_error_message(error, show_traceback, using_user_site): + """Format an error message for an EnvironmentError + + It may occur anytime during the execution of the install command. + """ + parts = [] + + # Mention the error if we are not going to show a traceback + parts.append("Could not install packages due to an EnvironmentError") + if not show_traceback: + parts.append(": ") + parts.append(str(error)) + else: + parts.append(".") + + # Spilt the error indication from a helper message (if any) + parts[-1] += "\n" + + # Suggest useful actions to the user: + # (1) using user site-packages or (2) verifying the permissions + if error.errno == errno.EACCES: + user_option_part = "Consider using the `--user` option" + permissions_part = "Check the permissions" + + if not using_user_site: + parts.extend([ + user_option_part, " or ", + permissions_part.lower(), + ]) + else: + parts.append(permissions_part) + parts.append(".\n") + + return "".join(parts).strip() + "\n" diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/list.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/list.py new file mode 100644 index 0000000..c6eeca7 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/list.py @@ -0,0 +1,306 @@ +from __future__ import absolute_import + +import json +import logging + +from pip._vendor import six +from pip._vendor.six.moves import zip_longest + +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import Command +from pip._internal.exceptions import CommandError +from pip._internal.index import PackageFinder +from pip._internal.utils.misc import ( + dist_is_editable, get_installed_distributions, +) +from pip._internal.utils.packaging import get_installer + +logger = logging.getLogger(__name__) + + +class ListCommand(Command): + """ + List installed packages, including editables. + + Packages are listed in a case-insensitive sorted order. + """ + name = 'list' + usage = """ + %prog [options]""" + summary = 'List installed packages.' + + def __init__(self, *args, **kw): + super(ListCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option( + '-o', '--outdated', + action='store_true', + default=False, + help='List outdated packages') + cmd_opts.add_option( + '-u', '--uptodate', + action='store_true', + default=False, + help='List uptodate packages') + cmd_opts.add_option( + '-e', '--editable', + action='store_true', + default=False, + help='List editable projects.') + cmd_opts.add_option( + '-l', '--local', + action='store_true', + default=False, + help=('If in a virtualenv that has global access, do not list ' + 'globally-installed packages.'), + ) + self.cmd_opts.add_option( + '--user', + dest='user', + action='store_true', + default=False, + help='Only output packages installed in user-site.') + + cmd_opts.add_option( + '--pre', + action='store_true', + default=False, + help=("Include pre-release and development versions. By default, " + "pip only finds stable versions."), + ) + + cmd_opts.add_option( + '--format', + action='store', + dest='list_format', + default="columns", + choices=('columns', 'freeze', 'json'), + help="Select the output format among: columns (default), freeze, " + "or json", + ) + + cmd_opts.add_option( + '--not-required', + action='store_true', + dest='not_required', + help="List packages that are not dependencies of " + "installed packages.", + ) + + cmd_opts.add_option( + '--exclude-editable', + action='store_false', + dest='include_editable', + help='Exclude editable package from output.', + ) + cmd_opts.add_option( + '--include-editable', + action='store_true', + dest='include_editable', + help='Include editable package from output.', + default=True, + ) + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, self.parser + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, cmd_opts) + + def _build_package_finder(self, options, index_urls, session): + """ + Create a package finder appropriate to this list command. + """ + return PackageFinder( + find_links=options.find_links, + index_urls=index_urls, + allow_all_prereleases=options.pre, + trusted_hosts=options.trusted_hosts, + process_dependency_links=options.process_dependency_links, + session=session, + ) + + def run(self, options, args): + if options.outdated and options.uptodate: + raise CommandError( + "Options --outdated and --uptodate cannot be combined.") + + packages = get_installed_distributions( + local_only=options.local, + user_only=options.user, + editables_only=options.editable, + include_editables=options.include_editable, + ) + + if options.outdated: + packages = self.get_outdated(packages, options) + elif options.uptodate: + packages = self.get_uptodate(packages, options) + + if options.not_required: + packages = self.get_not_required(packages, options) + + self.output_package_listing(packages, options) + + def get_outdated(self, packages, options): + return [ + dist for dist in self.iter_packages_latest_infos(packages, options) + if dist.latest_version > dist.parsed_version + ] + + def get_uptodate(self, packages, options): + return [ + dist for dist in self.iter_packages_latest_infos(packages, options) + if dist.latest_version == dist.parsed_version + ] + + def get_not_required(self, packages, options): + dep_keys = set() + for dist in packages: + dep_keys.update(requirement.key for requirement in dist.requires()) + return {pkg for pkg in packages if pkg.key not in dep_keys} + + def iter_packages_latest_infos(self, packages, options): + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index: + logger.debug('Ignoring indexes: %s', ','.join(index_urls)) + index_urls = [] + + dependency_links = [] + for dist in packages: + if dist.has_metadata('dependency_links.txt'): + dependency_links.extend( + dist.get_metadata_lines('dependency_links.txt'), + ) + + with self._build_session(options) as session: + finder = self._build_package_finder(options, index_urls, session) + finder.add_dependency_links(dependency_links) + + for dist in packages: + typ = 'unknown' + all_candidates = finder.find_all_candidates(dist.key) + if not options.pre: + # Remove prereleases + all_candidates = [candidate for candidate in all_candidates + if not candidate.version.is_prerelease] + + if not all_candidates: + continue + best_candidate = max(all_candidates, + key=finder._candidate_sort_key) + remote_version = best_candidate.version + if best_candidate.location.is_wheel: + typ = 'wheel' + else: + typ = 'sdist' + # This is dirty but makes the rest of the code much cleaner + dist.latest_version = remote_version + dist.latest_filetype = typ + yield dist + + def output_package_listing(self, packages, options): + packages = sorted( + packages, + key=lambda dist: dist.project_name.lower(), + ) + if options.list_format == 'columns' and packages: + data, header = format_for_columns(packages, options) + self.output_package_listing_columns(data, header) + elif options.list_format == 'freeze': + for dist in packages: + if options.verbose >= 1: + logger.info("%s==%s (%s)", dist.project_name, + dist.version, dist.location) + else: + logger.info("%s==%s", dist.project_name, dist.version) + elif options.list_format == 'json': + logger.info(format_for_json(packages, options)) + + def output_package_listing_columns(self, data, header): + # insert the header first: we need to know the size of column names + if len(data) > 0: + data.insert(0, header) + + pkg_strings, sizes = tabulate(data) + + # Create and add a separator. + if len(data) > 0: + pkg_strings.insert(1, " ".join(map(lambda x: '-' * x, sizes))) + + for val in pkg_strings: + logger.info(val) + + +def tabulate(vals): + # From pfmoore on GitHub: + # https://github.com/pypa/pip/issues/3651#issuecomment-216932564 + assert len(vals) > 0 + + sizes = [0] * max(len(x) for x in vals) + for row in vals: + sizes = [max(s, len(str(c))) for s, c in zip_longest(sizes, row)] + + result = [] + for row in vals: + display = " ".join([str(c).ljust(s) if c is not None else '' + for s, c in zip_longest(sizes, row)]) + result.append(display) + + return result, sizes + + +def format_for_columns(pkgs, options): + """ + Convert the package data into something usable + by output_package_listing_columns. + """ + running_outdated = options.outdated + # Adjust the header for the `pip list --outdated` case. + if running_outdated: + header = ["Package", "Version", "Latest", "Type"] + else: + header = ["Package", "Version"] + + data = [] + if options.verbose >= 1 or any(dist_is_editable(x) for x in pkgs): + header.append("Location") + if options.verbose >= 1: + header.append("Installer") + + for proj in pkgs: + # if we're working on the 'outdated' list, separate out the + # latest_version and type + row = [proj.project_name, proj.version] + + if running_outdated: + row.append(proj.latest_version) + row.append(proj.latest_filetype) + + if options.verbose >= 1 or dist_is_editable(proj): + row.append(proj.location) + if options.verbose >= 1: + row.append(get_installer(proj)) + + data.append(row) + + return data, header + + +def format_for_json(packages, options): + data = [] + for dist in packages: + info = { + 'name': dist.project_name, + 'version': six.text_type(dist.version), + } + if options.verbose >= 1: + info['location'] = dist.location + info['installer'] = get_installer(dist) + if options.outdated: + info['latest_version'] = six.text_type(dist.latest_version) + info['latest_filetype'] = dist.latest_filetype + data.append(info) + return json.dumps(data) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/search.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/search.py new file mode 100644 index 0000000..c157a31 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/search.py @@ -0,0 +1,135 @@ +from __future__ import absolute_import + +import logging +import sys +import textwrap +from collections import OrderedDict + +from pip._vendor import pkg_resources +from pip._vendor.packaging.version import parse as parse_version +# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import +from pip._vendor.six.moves import xmlrpc_client # type: ignore + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS +from pip._internal.download import PipXmlrpcTransport +from pip._internal.exceptions import CommandError +from pip._internal.models.index import PyPI +from pip._internal.utils.compat import get_terminal_size +from pip._internal.utils.logging import indent_log + +logger = logging.getLogger(__name__) + + +class SearchCommand(Command): + """Search for PyPI packages whose name or summary contains .""" + name = 'search' + usage = """ + %prog [options] """ + summary = 'Search PyPI for packages.' + ignore_require_venv = True + + def __init__(self, *args, **kw): + super(SearchCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '-i', '--index', + dest='index', + metavar='URL', + default=PyPI.pypi_url, + help='Base URL of Python Package Index (default %default)') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + if not args: + raise CommandError('Missing required argument (search query).') + query = args + pypi_hits = self.search(query, options) + hits = transform_hits(pypi_hits) + + terminal_width = None + if sys.stdout.isatty(): + terminal_width = get_terminal_size()[0] + + print_results(hits, terminal_width=terminal_width) + if pypi_hits: + return SUCCESS + return NO_MATCHES_FOUND + + def search(self, query, options): + index_url = options.index + with self._build_session(options) as session: + transport = PipXmlrpcTransport(index_url, session) + pypi = xmlrpc_client.ServerProxy(index_url, transport) + hits = pypi.search({'name': query, 'summary': query}, 'or') + return hits + + +def transform_hits(hits): + """ + The list from pypi is really a list of versions. We want a list of + packages with the list of versions stored inline. This converts the + list from pypi into one we can use. + """ + packages = OrderedDict() + for hit in hits: + name = hit['name'] + summary = hit['summary'] + version = hit['version'] + + if name not in packages.keys(): + packages[name] = { + 'name': name, + 'summary': summary, + 'versions': [version], + } + else: + packages[name]['versions'].append(version) + + # if this is the highest version, replace summary and score + if version == highest_version(packages[name]['versions']): + packages[name]['summary'] = summary + + return list(packages.values()) + + +def print_results(hits, name_column_width=None, terminal_width=None): + if not hits: + return + if name_column_width is None: + name_column_width = max([ + len(hit['name']) + len(highest_version(hit.get('versions', ['-']))) + for hit in hits + ]) + 4 + + installed_packages = [p.project_name for p in pkg_resources.working_set] + for hit in hits: + name = hit['name'] + summary = hit['summary'] or '' + latest = highest_version(hit.get('versions', ['-'])) + if terminal_width is not None: + target_width = terminal_width - name_column_width - 5 + if target_width > 10: + # wrap and indent summary to fit terminal + summary = textwrap.wrap(summary, target_width) + summary = ('\n' + ' ' * (name_column_width + 3)).join(summary) + + line = '%-*s - %s' % (name_column_width, + '%s (%s)' % (name, latest), summary) + try: + logger.info(line) + if name in installed_packages: + dist = pkg_resources.get_distribution(name) + with indent_log(): + if dist.version == latest: + logger.info('INSTALLED: %s (latest)', dist.version) + else: + logger.info('INSTALLED: %s', dist.version) + logger.info('LATEST: %s', latest) + except UnicodeEncodeError: + pass + + +def highest_version(versions): + return max(versions, key=parse_version) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/show.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/show.py new file mode 100644 index 0000000..f92c9bc --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/show.py @@ -0,0 +1,168 @@ +from __future__ import absolute_import + +import logging +import os +from email.parser import FeedParser # type: ignore + +from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS + +logger = logging.getLogger(__name__) + + +class ShowCommand(Command): + """ + Show information about one or more installed packages. + + The output is in RFC-compliant mail header format. + """ + name = 'show' + usage = """ + %prog [options] ...""" + summary = 'Show information about installed packages.' + ignore_require_venv = True + + def __init__(self, *args, **kw): + super(ShowCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '-f', '--files', + dest='files', + action='store_true', + default=False, + help='Show the full list of installed files for each package.') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + if not args: + logger.warning('ERROR: Please provide a package name or names.') + return ERROR + query = args + + results = search_packages_info(query) + if not print_results( + results, list_files=options.files, verbose=options.verbose): + return ERROR + return SUCCESS + + +def search_packages_info(query): + """ + Gather details from installed distributions. Print distribution name, + version, location, and installed files. Installed files requires a + pip generated 'installed-files.txt' in the distributions '.egg-info' + directory. + """ + installed = {} + for p in pkg_resources.working_set: + installed[canonicalize_name(p.project_name)] = p + + query_names = [canonicalize_name(name) for name in query] + + for dist in [installed[pkg] for pkg in query_names if pkg in installed]: + package = { + 'name': dist.project_name, + 'version': dist.version, + 'location': dist.location, + 'requires': [dep.project_name for dep in dist.requires()], + } + file_list = None + metadata = None + if isinstance(dist, pkg_resources.DistInfoDistribution): + # RECORDs should be part of .dist-info metadatas + if dist.has_metadata('RECORD'): + lines = dist.get_metadata_lines('RECORD') + paths = [l.split(',')[0] for l in lines] + paths = [os.path.join(dist.location, p) for p in paths] + file_list = [os.path.relpath(p, dist.location) for p in paths] + + if dist.has_metadata('METADATA'): + metadata = dist.get_metadata('METADATA') + else: + # Otherwise use pip's log for .egg-info's + if dist.has_metadata('installed-files.txt'): + paths = dist.get_metadata_lines('installed-files.txt') + paths = [os.path.join(dist.egg_info, p) for p in paths] + file_list = [os.path.relpath(p, dist.location) for p in paths] + + if dist.has_metadata('PKG-INFO'): + metadata = dist.get_metadata('PKG-INFO') + + if dist.has_metadata('entry_points.txt'): + entry_points = dist.get_metadata_lines('entry_points.txt') + package['entry_points'] = entry_points + + if dist.has_metadata('INSTALLER'): + for line in dist.get_metadata_lines('INSTALLER'): + if line.strip(): + package['installer'] = line.strip() + break + + # @todo: Should pkg_resources.Distribution have a + # `get_pkg_info` method? + feed_parser = FeedParser() + feed_parser.feed(metadata) + pkg_info_dict = feed_parser.close() + for key in ('metadata-version', 'summary', + 'home-page', 'author', 'author-email', 'license'): + package[key] = pkg_info_dict.get(key) + + # It looks like FeedParser cannot deal with repeated headers + classifiers = [] + for line in metadata.splitlines(): + if line.startswith('Classifier: '): + classifiers.append(line[len('Classifier: '):]) + package['classifiers'] = classifiers + + if file_list: + package['files'] = sorted(file_list) + yield package + + +def print_results(distributions, list_files=False, verbose=False): + """ + Print the informations from installed distributions found. + """ + results_printed = False + for i, dist in enumerate(distributions): + results_printed = True + if i > 0: + logger.info("---") + + name = dist.get('name', '') + required_by = [ + pkg.project_name for pkg in pkg_resources.working_set + if name in [required.name for required in pkg.requires()] + ] + + logger.info("Name: %s", name) + logger.info("Version: %s", dist.get('version', '')) + logger.info("Summary: %s", dist.get('summary', '')) + logger.info("Home-page: %s", dist.get('home-page', '')) + logger.info("Author: %s", dist.get('author', '')) + logger.info("Author-email: %s", dist.get('author-email', '')) + logger.info("License: %s", dist.get('license', '')) + logger.info("Location: %s", dist.get('location', '')) + logger.info("Requires: %s", ', '.join(dist.get('requires', []))) + logger.info("Required-by: %s", ', '.join(required_by)) + + if verbose: + logger.info("Metadata-Version: %s", + dist.get('metadata-version', '')) + logger.info("Installer: %s", dist.get('installer', '')) + logger.info("Classifiers:") + for classifier in dist.get('classifiers', []): + logger.info(" %s", classifier) + logger.info("Entry-points:") + for entry in dist.get('entry_points', []): + logger.info(" %s", entry.strip()) + if list_files: + logger.info("Files:") + for line in dist.get('files', []): + logger.info(" %s", line.strip()) + if "files" not in dist: + logger.info("Cannot locate installed-files.txt") + return results_printed diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/uninstall.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/uninstall.py new file mode 100644 index 0000000..0cd6f54 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/uninstall.py @@ -0,0 +1,78 @@ +from __future__ import absolute_import + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cli.base_command import Command +from pip._internal.exceptions import InstallationError +from pip._internal.req import parse_requirements +from pip._internal.req.constructors import install_req_from_line +from pip._internal.utils.misc import protect_pip_from_modification_on_windows + + +class UninstallCommand(Command): + """ + Uninstall packages. + + pip is able to uninstall most installed packages. Known exceptions are: + + - Pure distutils packages installed with ``python setup.py install``, which + leave behind no metadata to determine what files were installed. + - Script wrappers installed by ``python setup.py develop``. + """ + name = 'uninstall' + usage = """ + %prog [options] ... + %prog [options] -r ...""" + summary = 'Uninstall packages.' + + def __init__(self, *args, **kw): + super(UninstallCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help='Uninstall all the packages listed in the given requirements ' + 'file. This option can be used multiple times.', + ) + self.cmd_opts.add_option( + '-y', '--yes', + dest='yes', + action='store_true', + help="Don't ask for confirmation of uninstall deletions.") + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + with self._build_session(options) as session: + reqs_to_uninstall = {} + for name in args: + req = install_req_from_line( + name, isolated=options.isolated_mode, + ) + if req.name: + reqs_to_uninstall[canonicalize_name(req.name)] = req + for filename in options.requirements: + for req in parse_requirements( + filename, + options=options, + session=session): + if req.name: + reqs_to_uninstall[canonicalize_name(req.name)] = req + if not reqs_to_uninstall: + raise InstallationError( + 'You must give at least one requirement to %(name)s (see ' + '"pip help %(name)s")' % dict(name=self.name) + ) + + protect_pip_from_modification_on_windows( + modifying_pip="pip" in reqs_to_uninstall + ) + + for req in reqs_to_uninstall.values(): + uninstall_pathset = req.uninstall( + auto_confirm=options.yes, verbose=self.verbosity > 0, + ) + if uninstall_pathset: + uninstall_pathset.commit() diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/wheel.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/wheel.py new file mode 100644 index 0000000..9c1f149 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/commands/wheel.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +import logging +import os + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import RequirementCommand +from pip._internal.exceptions import CommandError, PreviousBuildDirError +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req import RequirementSet +from pip._internal.req.req_tracker import RequirementTracker +from pip._internal.resolve import Resolver +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.wheel import WheelBuilder + +logger = logging.getLogger(__name__) + + +class WheelCommand(RequirementCommand): + """ + Build Wheel archives for your requirements and dependencies. + + Wheel is a built-package format, and offers the advantage of not + recompiling your software during every install. For more details, see the + wheel docs: https://wheel.readthedocs.io/en/latest/ + + Requirements: setuptools>=0.8, and wheel. + + 'pip wheel' uses the bdist_wheel setuptools extension from the wheel + package to build individual wheels. + + """ + + name = 'wheel' + usage = """ + %prog [options] ... + %prog [options] -r ... + %prog [options] [-e] ... + %prog [options] [-e] ... + %prog [options] ...""" + + summary = 'Build wheels from your requirements.' + + def __init__(self, *args, **kw): + super(WheelCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option( + '-w', '--wheel-dir', + dest='wheel_dir', + metavar='dir', + default=os.curdir, + help=("Build wheels into , where the default is the " + "current working directory."), + ) + cmd_opts.add_option(cmdoptions.no_binary()) + cmd_opts.add_option(cmdoptions.only_binary()) + cmd_opts.add_option(cmdoptions.prefer_binary()) + cmd_opts.add_option( + '--build-option', + dest='build_options', + metavar='options', + action='append', + help="Extra arguments to be supplied to 'setup.py bdist_wheel'.", + ) + cmd_opts.add_option(cmdoptions.no_build_isolation()) + cmd_opts.add_option(cmdoptions.constraints()) + cmd_opts.add_option(cmdoptions.editable()) + cmd_opts.add_option(cmdoptions.requirements()) + cmd_opts.add_option(cmdoptions.src()) + cmd_opts.add_option(cmdoptions.ignore_requires_python()) + cmd_opts.add_option(cmdoptions.no_deps()) + cmd_opts.add_option(cmdoptions.build_dir()) + cmd_opts.add_option(cmdoptions.progress_bar()) + + cmd_opts.add_option( + '--global-option', + dest='global_options', + action='append', + metavar='options', + help="Extra global options to be supplied to the setup.py " + "call before the 'bdist_wheel' command.") + + cmd_opts.add_option( + '--pre', + action='store_true', + default=False, + help=("Include pre-release and development versions. By default, " + "pip only finds stable versions."), + ) + + cmd_opts.add_option(cmdoptions.no_clean()) + cmd_opts.add_option(cmdoptions.require_hashes()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, cmd_opts) + + def run(self, options, args): + cmdoptions.check_install_build_global(options) + + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index: + logger.debug('Ignoring indexes: %s', ','.join(index_urls)) + index_urls = [] + + if options.build_dir: + options.build_dir = os.path.abspath(options.build_dir) + + options.src_dir = os.path.abspath(options.src_dir) + + with self._build_session(options) as session: + finder = self._build_package_finder(options, session) + build_delete = (not (options.no_clean or options.build_dir)) + wheel_cache = WheelCache(options.cache_dir, options.format_control) + + with RequirementTracker() as req_tracker, TempDirectory( + options.build_dir, delete=build_delete, kind="wheel" + ) as directory: + + requirement_set = RequirementSet( + require_hashes=options.require_hashes, + ) + + try: + self.populate_requirement_set( + requirement_set, args, options, finder, session, + self.name, wheel_cache + ) + + preparer = RequirementPreparer( + build_dir=directory.path, + src_dir=options.src_dir, + download_dir=None, + wheel_download_dir=options.wheel_dir, + progress_bar=options.progress_bar, + build_isolation=options.build_isolation, + req_tracker=req_tracker, + ) + + resolver = Resolver( + preparer=preparer, + finder=finder, + session=session, + wheel_cache=wheel_cache, + use_user_site=False, + upgrade_strategy="to-satisfy-only", + force_reinstall=False, + ignore_dependencies=options.ignore_dependencies, + ignore_requires_python=options.ignore_requires_python, + ignore_installed=True, + isolated=options.isolated_mode, + ) + resolver.resolve(requirement_set) + + # build wheels + wb = WheelBuilder( + finder, preparer, wheel_cache, + build_options=options.build_options or [], + global_options=options.global_options or [], + no_clean=options.no_clean, + ) + wheels_built_successfully = wb.build( + requirement_set.requirements.values(), session=session, + ) + if not wheels_built_successfully: + raise CommandError( + "Failed to build one or more wheels" + ) + except PreviousBuildDirError: + options.no_clean = True + raise + finally: + if not options.no_clean: + requirement_set.cleanup_files() + wheel_cache.cleanup() diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/configuration.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/configuration.py new file mode 100644 index 0000000..fe6df9b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/configuration.py @@ -0,0 +1,387 @@ +"""Configuration management setup + +Some terminology: +- name + As written in config files. +- value + Value associated with a name +- key + Name combined with it's section (section.name) +- variant + A single word describing where the configuration key-value pair came from +""" + +import locale +import logging +import os + +from pip._vendor import six +from pip._vendor.six.moves import configparser + +from pip._internal.exceptions import ( + ConfigurationError, ConfigurationFileCouldNotBeLoaded, +) +from pip._internal.locations import ( + legacy_config_file, new_config_file, running_under_virtualenv, + site_config_files, venv_config_file, +) +from pip._internal.utils.misc import ensure_dir, enum +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( # noqa: F401 + Any, Dict, Iterable, List, NewType, Optional, Tuple + ) + + RawConfigParser = configparser.RawConfigParser # Shorthand + Kind = NewType("Kind", str) + +logger = logging.getLogger(__name__) + + +# NOTE: Maybe use the optionx attribute to normalize keynames. +def _normalize_name(name): + # type: (str) -> str + """Make a name consistent regardless of source (environment or file) + """ + name = name.lower().replace('_', '-') + if name.startswith('--'): + name = name[2:] # only prefer long opts + return name + + +def _disassemble_key(name): + # type: (str) -> List[str] + return name.split(".", 1) + + +# The kinds of configurations there are. +kinds = enum( + USER="user", # User Specific + GLOBAL="global", # System Wide + VENV="venv", # Virtual Environment Specific + ENV="env", # from PIP_CONFIG_FILE + ENV_VAR="env-var", # from Environment Variables +) + + +class Configuration(object): + """Handles management of configuration. + + Provides an interface to accessing and managing configuration files. + + This class converts provides an API that takes "section.key-name" style + keys and stores the value associated with it as "key-name" under the + section "section". + + This allows for a clean interface wherein the both the section and the + key-name are preserved in an easy to manage form in the configuration files + and the data stored is also nice. + """ + + def __init__(self, isolated, load_only=None): + # type: (bool, Kind) -> None + super(Configuration, self).__init__() + + _valid_load_only = [kinds.USER, kinds.GLOBAL, kinds.VENV, None] + if load_only not in _valid_load_only: + raise ConfigurationError( + "Got invalid value for load_only - should be one of {}".format( + ", ".join(map(repr, _valid_load_only[:-1])) + ) + ) + self.isolated = isolated # type: bool + self.load_only = load_only # type: Optional[Kind] + + # The order here determines the override order. + self._override_order = [ + kinds.GLOBAL, kinds.USER, kinds.VENV, kinds.ENV, kinds.ENV_VAR + ] + + self._ignore_env_names = ["version", "help"] + + # Because we keep track of where we got the data from + self._parsers = { + variant: [] for variant in self._override_order + } # type: Dict[Kind, List[Tuple[str, RawConfigParser]]] + self._config = { + variant: {} for variant in self._override_order + } # type: Dict[Kind, Dict[str, Any]] + self._modified_parsers = [] # type: List[Tuple[str, RawConfigParser]] + + def load(self): + # type: () -> None + """Loads configuration from configuration files and environment + """ + self._load_config_files() + if not self.isolated: + self._load_environment_vars() + + def get_file_to_edit(self): + # type: () -> Optional[str] + """Returns the file with highest priority in configuration + """ + assert self.load_only is not None, \ + "Need to be specified a file to be editing" + + try: + return self._get_parser_to_modify()[0] + except IndexError: + return None + + def items(self): + # type: () -> Iterable[Tuple[str, Any]] + """Returns key-value pairs like dict.items() representing the loaded + configuration + """ + return self._dictionary.items() + + def get_value(self, key): + # type: (str) -> Any + """Get a value from the configuration. + """ + try: + return self._dictionary[key] + except KeyError: + raise ConfigurationError("No such key - {}".format(key)) + + def set_value(self, key, value): + # type: (str, Any) -> None + """Modify a value in the configuration. + """ + self._ensure_have_load_only() + + fname, parser = self._get_parser_to_modify() + + if parser is not None: + section, name = _disassemble_key(key) + + # Modify the parser and the configuration + if not parser.has_section(section): + parser.add_section(section) + parser.set(section, name, value) + + self._config[self.load_only][key] = value + self._mark_as_modified(fname, parser) + + def unset_value(self, key): + # type: (str) -> None + """Unset a value in the configuration. + """ + self._ensure_have_load_only() + + if key not in self._config[self.load_only]: + raise ConfigurationError("No such key - {}".format(key)) + + fname, parser = self._get_parser_to_modify() + + if parser is not None: + section, name = _disassemble_key(key) + + # Remove the key in the parser + modified_something = False + if parser.has_section(section): + # Returns whether the option was removed or not + modified_something = parser.remove_option(section, name) + + if modified_something: + # name removed from parser, section may now be empty + section_iter = iter(parser.items(section)) + try: + val = six.next(section_iter) + except StopIteration: + val = None + + if val is None: + parser.remove_section(section) + + self._mark_as_modified(fname, parser) + else: + raise ConfigurationError( + "Fatal Internal error [id=1]. Please report as a bug." + ) + + del self._config[self.load_only][key] + + def save(self): + # type: () -> None + """Save the currentin-memory state. + """ + self._ensure_have_load_only() + + for fname, parser in self._modified_parsers: + logger.info("Writing to %s", fname) + + # Ensure directory exists. + ensure_dir(os.path.dirname(fname)) + + with open(fname, "w") as f: + parser.write(f) # type: ignore + + # + # Private routines + # + + def _ensure_have_load_only(self): + # type: () -> None + if self.load_only is None: + raise ConfigurationError("Needed a specific file to be modifying.") + logger.debug("Will be working with %s variant only", self.load_only) + + @property + def _dictionary(self): + # type: () -> Dict[str, Any] + """A dictionary representing the loaded configuration. + """ + # NOTE: Dictionaries are not populated if not loaded. So, conditionals + # are not needed here. + retval = {} + + for variant in self._override_order: + retval.update(self._config[variant]) + + return retval + + def _load_config_files(self): + # type: () -> None + """Loads configuration from configuration files + """ + config_files = dict(self._iter_config_files()) + if config_files[kinds.ENV][0:1] == [os.devnull]: + logger.debug( + "Skipping loading configuration files due to " + "environment's PIP_CONFIG_FILE being os.devnull" + ) + return + + for variant, files in config_files.items(): + for fname in files: + # If there's specific variant set in `load_only`, load only + # that variant, not the others. + if self.load_only is not None and variant != self.load_only: + logger.debug( + "Skipping file '%s' (variant: %s)", fname, variant + ) + continue + + parser = self._load_file(variant, fname) + + # Keeping track of the parsers used + self._parsers[variant].append((fname, parser)) + + def _load_file(self, variant, fname): + # type: (Kind, str) -> RawConfigParser + logger.debug("For variant '%s', will try loading '%s'", variant, fname) + parser = self._construct_parser(fname) + + for section in parser.sections(): + items = parser.items(section) + self._config[variant].update(self._normalized_keys(section, items)) + + return parser + + def _construct_parser(self, fname): + # type: (str) -> RawConfigParser + parser = configparser.RawConfigParser() + # If there is no such file, don't bother reading it but create the + # parser anyway, to hold the data. + # Doing this is useful when modifying and saving files, where we don't + # need to construct a parser. + if os.path.exists(fname): + try: + parser.read(fname) + except UnicodeDecodeError: + # See https://github.com/pypa/pip/issues/4963 + raise ConfigurationFileCouldNotBeLoaded( + reason="contains invalid {} characters".format( + locale.getpreferredencoding(False) + ), + fname=fname, + ) + except configparser.Error as error: + # See https://github.com/pypa/pip/issues/4893 + raise ConfigurationFileCouldNotBeLoaded(error=error) + return parser + + def _load_environment_vars(self): + # type: () -> None + """Loads configuration from environment variables + """ + self._config[kinds.ENV_VAR].update( + self._normalized_keys(":env:", self._get_environ_vars()) + ) + + def _normalized_keys(self, section, items): + # type: (str, Iterable[Tuple[str, Any]]) -> Dict[str, Any] + """Normalizes items to construct a dictionary with normalized keys. + + This routine is where the names become keys and are made the same + regardless of source - configuration files or environment. + """ + normalized = {} + for name, val in items: + key = section + "." + _normalize_name(name) + normalized[key] = val + return normalized + + def _get_environ_vars(self): + # type: () -> Iterable[Tuple[str, str]] + """Returns a generator with all environmental vars with prefix PIP_""" + for key, val in os.environ.items(): + should_be_yielded = ( + key.startswith("PIP_") and + key[4:].lower() not in self._ignore_env_names + ) + if should_be_yielded: + yield key[4:].lower(), val + + # XXX: This is patched in the tests. + def _iter_config_files(self): + # type: () -> Iterable[Tuple[Kind, List[str]]] + """Yields variant and configuration files associated with it. + + This should be treated like items of a dictionary. + """ + # SMELL: Move the conditions out of this function + + # environment variables have the lowest priority + config_file = os.environ.get('PIP_CONFIG_FILE', None) + if config_file is not None: + yield kinds.ENV, [config_file] + else: + yield kinds.ENV, [] + + # at the base we have any global configuration + yield kinds.GLOBAL, list(site_config_files) + + # per-user configuration next + should_load_user_config = not self.isolated and not ( + config_file and os.path.exists(config_file) + ) + if should_load_user_config: + # The legacy config file is overridden by the new config file + yield kinds.USER, [legacy_config_file, new_config_file] + + # finally virtualenv configuration first trumping others + if running_under_virtualenv(): + yield kinds.VENV, [venv_config_file] + + def _get_parser_to_modify(self): + # type: () -> Tuple[str, RawConfigParser] + # Determine which parser to modify + parsers = self._parsers[self.load_only] + if not parsers: + # This should not happen if everything works correctly. + raise ConfigurationError( + "Fatal Internal error [id=2]. Please report as a bug." + ) + + # Use the highest priority parser. + return parsers[-1] + + # XXX: This is patched in the tests. + def _mark_as_modified(self, fname, parser): + # type: (str, RawConfigParser) -> None + file_parser_tuple = (fname, parser) + if file_parser_tuple not in self._modified_parsers: + self._modified_parsers.append(file_parser_tuple) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/download.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/download.py new file mode 100644 index 0000000..96f3b65 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/download.py @@ -0,0 +1,921 @@ +from __future__ import absolute_import + +import cgi +import email.utils +import getpass +import json +import logging +import mimetypes +import os +import platform +import re +import shutil +import sys + +from pip._vendor import requests, six, urllib3 +from pip._vendor.cachecontrol import CacheControlAdapter +from pip._vendor.cachecontrol.caches import FileCache +from pip._vendor.lockfile import LockError +from pip._vendor.requests.adapters import BaseAdapter, HTTPAdapter +from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response +from pip._vendor.requests.structures import CaseInsensitiveDict +from pip._vendor.requests.utils import get_netrc_auth +# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import +from pip._vendor.six.moves import xmlrpc_client # type: ignore +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request +from pip._vendor.six.moves.urllib.parse import unquote as urllib_unquote +from pip._vendor.urllib3.util import IS_PYOPENSSL + +import pip +from pip._internal.exceptions import HashMismatch, InstallationError +from pip._internal.locations import write_delete_marker_file +from pip._internal.models.index import PyPI +from pip._internal.utils.encoding import auto_decode +from pip._internal.utils.filesystem import check_path_owner +from pip._internal.utils.glibc import libc_ver +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + ARCHIVE_EXTENSIONS, ask_path_exists, backup_dir, call_subprocess, consume, + display_path, format_size, get_installed_version, rmtree, splitext, + unpack_file, +) +from pip._internal.utils.setuptools_build import SETUPTOOLS_SHIM +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.ui import DownloadProgressProvider +from pip._internal.vcs import vcs + +try: + import ssl # noqa +except ImportError: + ssl = None + +HAS_TLS = (ssl is not None) or IS_PYOPENSSL + +__all__ = ['get_file_content', + 'is_url', 'url_to_path', 'path_to_url', + 'is_archive_file', 'unpack_vcs_link', + 'unpack_file_url', 'is_vcs_url', 'is_file_url', + 'unpack_http_url', 'unpack_url'] + + +logger = logging.getLogger(__name__) + + +def user_agent(): + """ + Return a string representing the user agent. + """ + data = { + "installer": {"name": "pip", "version": pip.__version__}, + "python": platform.python_version(), + "implementation": { + "name": platform.python_implementation(), + }, + } + + if data["implementation"]["name"] == 'CPython': + data["implementation"]["version"] = platform.python_version() + elif data["implementation"]["name"] == 'PyPy': + if sys.pypy_version_info.releaselevel == 'final': + pypy_version_info = sys.pypy_version_info[:3] + else: + pypy_version_info = sys.pypy_version_info + data["implementation"]["version"] = ".".join( + [str(x) for x in pypy_version_info] + ) + elif data["implementation"]["name"] == 'Jython': + # Complete Guess + data["implementation"]["version"] = platform.python_version() + elif data["implementation"]["name"] == 'IronPython': + # Complete Guess + data["implementation"]["version"] = platform.python_version() + + if sys.platform.startswith("linux"): + from pip._vendor import distro + distro_infos = dict(filter( + lambda x: x[1], + zip(["name", "version", "id"], distro.linux_distribution()), + )) + libc = dict(filter( + lambda x: x[1], + zip(["lib", "version"], libc_ver()), + )) + if libc: + distro_infos["libc"] = libc + if distro_infos: + data["distro"] = distro_infos + + if sys.platform.startswith("darwin") and platform.mac_ver()[0]: + data["distro"] = {"name": "macOS", "version": platform.mac_ver()[0]} + + if platform.system(): + data.setdefault("system", {})["name"] = platform.system() + + if platform.release(): + data.setdefault("system", {})["release"] = platform.release() + + if platform.machine(): + data["cpu"] = platform.machine() + + if HAS_TLS: + data["openssl_version"] = ssl.OPENSSL_VERSION + + setuptools_version = get_installed_version("setuptools") + if setuptools_version is not None: + data["setuptools_version"] = setuptools_version + + return "{data[installer][name]}/{data[installer][version]} {json}".format( + data=data, + json=json.dumps(data, separators=(",", ":"), sort_keys=True), + ) + + +class MultiDomainBasicAuth(AuthBase): + + def __init__(self, prompting=True): + self.prompting = prompting + self.passwords = {} + + def __call__(self, req): + parsed = urllib_parse.urlparse(req.url) + + # Get the netloc without any embedded credentials + netloc = parsed.netloc.rsplit("@", 1)[-1] + + # Set the url of the request to the url without any credentials + req.url = urllib_parse.urlunparse(parsed[:1] + (netloc,) + parsed[2:]) + + # Use any stored credentials that we have for this netloc + username, password = self.passwords.get(netloc, (None, None)) + + # Extract credentials embedded in the url if we have none stored + if username is None: + username, password = self.parse_credentials(parsed.netloc) + + # Get creds from netrc if we still don't have them + if username is None and password is None: + netrc_auth = get_netrc_auth(req.url) + username, password = netrc_auth if netrc_auth else (None, None) + + if username or password: + # Store the username and password + self.passwords[netloc] = (username, password) + + # Send the basic auth with this request + req = HTTPBasicAuth(username or "", password or "")(req) + + # Attach a hook to handle 401 responses + req.register_hook("response", self.handle_401) + + return req + + def handle_401(self, resp, **kwargs): + # We only care about 401 responses, anything else we want to just + # pass through the actual response + if resp.status_code != 401: + return resp + + # We are not able to prompt the user so simply return the response + if not self.prompting: + return resp + + parsed = urllib_parse.urlparse(resp.url) + + # Prompt the user for a new username and password + username = six.moves.input("User for %s: " % parsed.netloc) + password = getpass.getpass("Password: ") + + # Store the new username and password to use for future requests + if username or password: + self.passwords[parsed.netloc] = (username, password) + + # Consume content and release the original connection to allow our new + # request to reuse the same one. + resp.content + resp.raw.release_conn() + + # Add our new username and password to the request + req = HTTPBasicAuth(username or "", password or "")(resp.request) + + # Send our new request + new_resp = resp.connection.send(req, **kwargs) + new_resp.history.append(resp) + + return new_resp + + def parse_credentials(self, netloc): + if "@" in netloc: + userinfo = netloc.rsplit("@", 1)[0] + if ":" in userinfo: + user, pwd = userinfo.split(":", 1) + return (urllib_unquote(user), urllib_unquote(pwd)) + return urllib_unquote(userinfo), None + return None, None + + +class LocalFSAdapter(BaseAdapter): + + def send(self, request, stream=None, timeout=None, verify=None, cert=None, + proxies=None): + pathname = url_to_path(request.url) + + resp = Response() + resp.status_code = 200 + resp.url = request.url + + try: + stats = os.stat(pathname) + except OSError as exc: + resp.status_code = 404 + resp.raw = exc + else: + modified = email.utils.formatdate(stats.st_mtime, usegmt=True) + content_type = mimetypes.guess_type(pathname)[0] or "text/plain" + resp.headers = CaseInsensitiveDict({ + "Content-Type": content_type, + "Content-Length": stats.st_size, + "Last-Modified": modified, + }) + + resp.raw = open(pathname, "rb") + resp.close = resp.raw.close + + return resp + + def close(self): + pass + + +class SafeFileCache(FileCache): + """ + A file based cache which is safe to use even when the target directory may + not be accessible or writable. + """ + + def __init__(self, *args, **kwargs): + super(SafeFileCache, self).__init__(*args, **kwargs) + + # Check to ensure that the directory containing our cache directory + # is owned by the user current executing pip. If it does not exist + # we will check the parent directory until we find one that does exist. + # If it is not owned by the user executing pip then we will disable + # the cache and log a warning. + if not check_path_owner(self.directory): + logger.warning( + "The directory '%s' or its parent directory is not owned by " + "the current user and the cache has been disabled. Please " + "check the permissions and owner of that directory. If " + "executing pip with sudo, you may want sudo's -H flag.", + self.directory, + ) + + # Set our directory to None to disable the Cache + self.directory = None + + def get(self, *args, **kwargs): + # If we don't have a directory, then the cache should be a no-op. + if self.directory is None: + return + + try: + return super(SafeFileCache, self).get(*args, **kwargs) + except (LockError, OSError, IOError): + # We intentionally silence this error, if we can't access the cache + # then we can just skip caching and process the request as if + # caching wasn't enabled. + pass + + def set(self, *args, **kwargs): + # If we don't have a directory, then the cache should be a no-op. + if self.directory is None: + return + + try: + return super(SafeFileCache, self).set(*args, **kwargs) + except (LockError, OSError, IOError): + # We intentionally silence this error, if we can't access the cache + # then we can just skip caching and process the request as if + # caching wasn't enabled. + pass + + def delete(self, *args, **kwargs): + # If we don't have a directory, then the cache should be a no-op. + if self.directory is None: + return + + try: + return super(SafeFileCache, self).delete(*args, **kwargs) + except (LockError, OSError, IOError): + # We intentionally silence this error, if we can't access the cache + # then we can just skip caching and process the request as if + # caching wasn't enabled. + pass + + +class InsecureHTTPAdapter(HTTPAdapter): + + def cert_verify(self, conn, url, verify, cert): + conn.cert_reqs = 'CERT_NONE' + conn.ca_certs = None + + +class PipSession(requests.Session): + + timeout = None + + def __init__(self, *args, **kwargs): + retries = kwargs.pop("retries", 0) + cache = kwargs.pop("cache", None) + insecure_hosts = kwargs.pop("insecure_hosts", []) + + super(PipSession, self).__init__(*args, **kwargs) + + # Attach our User Agent to the request + self.headers["User-Agent"] = user_agent() + + # Attach our Authentication handler to the session + self.auth = MultiDomainBasicAuth() + + # Create our urllib3.Retry instance which will allow us to customize + # how we handle retries. + retries = urllib3.Retry( + # Set the total number of retries that a particular request can + # have. + total=retries, + + # A 503 error from PyPI typically means that the Fastly -> Origin + # connection got interrupted in some way. A 503 error in general + # is typically considered a transient error so we'll go ahead and + # retry it. + # A 500 may indicate transient error in Amazon S3 + # A 520 or 527 - may indicate transient error in CloudFlare + status_forcelist=[500, 503, 520, 527], + + # Add a small amount of back off between failed requests in + # order to prevent hammering the service. + backoff_factor=0.25, + ) + + # We want to _only_ cache responses on securely fetched origins. We do + # this because we can't validate the response of an insecurely fetched + # origin, and we don't want someone to be able to poison the cache and + # require manual eviction from the cache to fix it. + if cache: + secure_adapter = CacheControlAdapter( + cache=SafeFileCache(cache, use_dir_lock=True), + max_retries=retries, + ) + else: + secure_adapter = HTTPAdapter(max_retries=retries) + + # Our Insecure HTTPAdapter disables HTTPS validation. It does not + # support caching (see above) so we'll use it for all http:// URLs as + # well as any https:// host that we've marked as ignoring TLS errors + # for. + insecure_adapter = InsecureHTTPAdapter(max_retries=retries) + + self.mount("https://", secure_adapter) + self.mount("http://", insecure_adapter) + + # Enable file:// urls + self.mount("file://", LocalFSAdapter()) + + # We want to use a non-validating adapter for any requests which are + # deemed insecure. + for host in insecure_hosts: + self.mount("https://{}/".format(host), insecure_adapter) + + def request(self, method, url, *args, **kwargs): + # Allow setting a default timeout on a session + kwargs.setdefault("timeout", self.timeout) + + # Dispatch the actual request + return super(PipSession, self).request(method, url, *args, **kwargs) + + +def get_file_content(url, comes_from=None, session=None): + """Gets the content of a file; it may be a filename, file: URL, or + http: URL. Returns (location, content). Content is unicode. + + :param url: File path or url. + :param comes_from: Origin description of requirements. + :param session: Instance of pip.download.PipSession. + """ + if session is None: + raise TypeError( + "get_file_content() missing 1 required keyword argument: 'session'" + ) + + match = _scheme_re.search(url) + if match: + scheme = match.group(1).lower() + if (scheme == 'file' and comes_from and + comes_from.startswith('http')): + raise InstallationError( + 'Requirements file %s references URL %s, which is local' + % (comes_from, url)) + if scheme == 'file': + path = url.split(':', 1)[1] + path = path.replace('\\', '/') + match = _url_slash_drive_re.match(path) + if match: + path = match.group(1) + ':' + path.split('|', 1)[1] + path = urllib_parse.unquote(path) + if path.startswith('/'): + path = '/' + path.lstrip('/') + url = path + else: + # FIXME: catch some errors + resp = session.get(url) + resp.raise_for_status() + return resp.url, resp.text + try: + with open(url, 'rb') as f: + content = auto_decode(f.read()) + except IOError as exc: + raise InstallationError( + 'Could not open requirements file: %s' % str(exc) + ) + return url, content + + +_scheme_re = re.compile(r'^(http|https|file):', re.I) +_url_slash_drive_re = re.compile(r'/*([a-z])\|', re.I) + + +def is_url(name): + """Returns true if the name looks like a URL""" + if ':' not in name: + return False + scheme = name.split(':', 1)[0].lower() + return scheme in ['http', 'https', 'file', 'ftp'] + vcs.all_schemes + + +def url_to_path(url): + """ + Convert a file: URL to a path. + """ + assert url.startswith('file:'), ( + "You can only turn file: urls into filenames (not %r)" % url) + + _, netloc, path, _, _ = urllib_parse.urlsplit(url) + + # if we have a UNC path, prepend UNC share notation + if netloc: + netloc = '\\\\' + netloc + + path = urllib_request.url2pathname(netloc + path) + return path + + +def path_to_url(path): + """ + Convert a path to a file: URL. The path will be made absolute and have + quoted path parts. + """ + path = os.path.normpath(os.path.abspath(path)) + url = urllib_parse.urljoin('file:', urllib_request.pathname2url(path)) + return url + + +def is_archive_file(name): + """Return True if `name` is a considered as an archive file.""" + ext = splitext(name)[1].lower() + if ext in ARCHIVE_EXTENSIONS: + return True + return False + + +def unpack_vcs_link(link, location): + vcs_backend = _get_used_vcs_backend(link) + vcs_backend.unpack(location) + + +def _get_used_vcs_backend(link): + for backend in vcs.backends: + if link.scheme in backend.schemes: + vcs_backend = backend(link.url) + return vcs_backend + + +def is_vcs_url(link): + return bool(_get_used_vcs_backend(link)) + + +def is_file_url(link): + return link.url.lower().startswith('file:') + + +def is_dir_url(link): + """Return whether a file:// Link points to a directory. + + ``link`` must not have any other scheme but file://. Call is_file_url() + first. + + """ + link_path = url_to_path(link.url_without_fragment) + return os.path.isdir(link_path) + + +def _progress_indicator(iterable, *args, **kwargs): + return iterable + + +def _download_url(resp, link, content_file, hashes, progress_bar): + try: + total_length = int(resp.headers['content-length']) + except (ValueError, KeyError, TypeError): + total_length = 0 + + cached_resp = getattr(resp, "from_cache", False) + if logger.getEffectiveLevel() > logging.INFO: + show_progress = False + elif cached_resp: + show_progress = False + elif total_length > (40 * 1000): + show_progress = True + elif not total_length: + show_progress = True + else: + show_progress = False + + show_url = link.show_url + + def resp_read(chunk_size): + try: + # Special case for urllib3. + for chunk in resp.raw.stream( + chunk_size, + # We use decode_content=False here because we don't + # want urllib3 to mess with the raw bytes we get + # from the server. If we decompress inside of + # urllib3 then we cannot verify the checksum + # because the checksum will be of the compressed + # file. This breakage will only occur if the + # server adds a Content-Encoding header, which + # depends on how the server was configured: + # - Some servers will notice that the file isn't a + # compressible file and will leave the file alone + # and with an empty Content-Encoding + # - Some servers will notice that the file is + # already compressed and will leave the file + # alone and will add a Content-Encoding: gzip + # header + # - Some servers won't notice anything at all and + # will take a file that's already been compressed + # and compress it again and set the + # Content-Encoding: gzip header + # + # By setting this not to decode automatically we + # hope to eliminate problems with the second case. + decode_content=False): + yield chunk + except AttributeError: + # Standard file-like object. + while True: + chunk = resp.raw.read(chunk_size) + if not chunk: + break + yield chunk + + def written_chunks(chunks): + for chunk in chunks: + content_file.write(chunk) + yield chunk + + progress_indicator = _progress_indicator + + if link.netloc == PyPI.netloc: + url = show_url + else: + url = link.url_without_fragment + + if show_progress: # We don't show progress on cached responses + progress_indicator = DownloadProgressProvider(progress_bar, + max=total_length) + if total_length: + logger.info("Downloading %s (%s)", url, format_size(total_length)) + else: + logger.info("Downloading %s", url) + elif cached_resp: + logger.info("Using cached %s", url) + else: + logger.info("Downloading %s", url) + + logger.debug('Downloading from URL %s', link) + + downloaded_chunks = written_chunks( + progress_indicator( + resp_read(CONTENT_CHUNK_SIZE), + CONTENT_CHUNK_SIZE + ) + ) + if hashes: + hashes.check_against_chunks(downloaded_chunks) + else: + consume(downloaded_chunks) + + +def _copy_file(filename, location, link): + copy = True + download_location = os.path.join(location, link.filename) + if os.path.exists(download_location): + response = ask_path_exists( + 'The file %s exists. (i)gnore, (w)ipe, (b)ackup, (a)abort' % + display_path(download_location), ('i', 'w', 'b', 'a')) + if response == 'i': + copy = False + elif response == 'w': + logger.warning('Deleting %s', display_path(download_location)) + os.remove(download_location) + elif response == 'b': + dest_file = backup_dir(download_location) + logger.warning( + 'Backing up %s to %s', + display_path(download_location), + display_path(dest_file), + ) + shutil.move(download_location, dest_file) + elif response == 'a': + sys.exit(-1) + if copy: + shutil.copy(filename, download_location) + logger.info('Saved %s', display_path(download_location)) + + +def unpack_http_url(link, location, download_dir=None, + session=None, hashes=None, progress_bar="on"): + if session is None: + raise TypeError( + "unpack_http_url() missing 1 required keyword argument: 'session'" + ) + + with TempDirectory(kind="unpack") as temp_dir: + # If a download dir is specified, is the file already downloaded there? + already_downloaded_path = None + if download_dir: + already_downloaded_path = _check_download_dir(link, + download_dir, + hashes) + + if already_downloaded_path: + from_path = already_downloaded_path + content_type = mimetypes.guess_type(from_path)[0] + else: + # let's download to a tmp dir + from_path, content_type = _download_http_url(link, + session, + temp_dir.path, + hashes, + progress_bar) + + # unpack the archive to the build dir location. even when only + # downloading archives, they have to be unpacked to parse dependencies + unpack_file(from_path, location, content_type, link) + + # a download dir is specified; let's copy the archive there + if download_dir and not already_downloaded_path: + _copy_file(from_path, download_dir, link) + + if not already_downloaded_path: + os.unlink(from_path) + + +def unpack_file_url(link, location, download_dir=None, hashes=None): + """Unpack link into location. + + If download_dir is provided and link points to a file, make a copy + of the link file inside download_dir. + """ + link_path = url_to_path(link.url_without_fragment) + + # If it's a url to a local directory + if is_dir_url(link): + if os.path.isdir(location): + rmtree(location) + shutil.copytree(link_path, location, symlinks=True) + if download_dir: + logger.info('Link is a directory, ignoring download_dir') + return + + # If --require-hashes is off, `hashes` is either empty, the + # link's embedded hash, or MissingHashes; it is required to + # match. If --require-hashes is on, we are satisfied by any + # hash in `hashes` matching: a URL-based or an option-based + # one; no internet-sourced hash will be in `hashes`. + if hashes: + hashes.check_against_path(link_path) + + # If a download dir is specified, is the file already there and valid? + already_downloaded_path = None + if download_dir: + already_downloaded_path = _check_download_dir(link, + download_dir, + hashes) + + if already_downloaded_path: + from_path = already_downloaded_path + else: + from_path = link_path + + content_type = mimetypes.guess_type(from_path)[0] + + # unpack the archive to the build dir location. even when only downloading + # archives, they have to be unpacked to parse dependencies + unpack_file(from_path, location, content_type, link) + + # a download dir is specified and not already downloaded + if download_dir and not already_downloaded_path: + _copy_file(from_path, download_dir, link) + + +def _copy_dist_from_dir(link_path, location): + """Copy distribution files in `link_path` to `location`. + + Invoked when user requests to install a local directory. E.g.: + + pip install . + pip install ~/dev/git-repos/python-prompt-toolkit + + """ + + # Note: This is currently VERY SLOW if you have a lot of data in the + # directory, because it copies everything with `shutil.copytree`. + # What it should really do is build an sdist and install that. + # See https://github.com/pypa/pip/issues/2195 + + if os.path.isdir(location): + rmtree(location) + + # build an sdist + setup_py = 'setup.py' + sdist_args = [sys.executable] + sdist_args.append('-c') + sdist_args.append(SETUPTOOLS_SHIM % setup_py) + sdist_args.append('sdist') + sdist_args += ['--dist-dir', location] + logger.info('Running setup.py sdist for %s', link_path) + + with indent_log(): + call_subprocess(sdist_args, cwd=link_path, show_stdout=False) + + # unpack sdist into `location` + sdist = os.path.join(location, os.listdir(location)[0]) + logger.info('Unpacking sdist %s into %s', sdist, location) + unpack_file(sdist, location, content_type=None, link=None) + + +class PipXmlrpcTransport(xmlrpc_client.Transport): + """Provide a `xmlrpclib.Transport` implementation via a `PipSession` + object. + """ + + def __init__(self, index_url, session, use_datetime=False): + xmlrpc_client.Transport.__init__(self, use_datetime) + index_parts = urllib_parse.urlparse(index_url) + self._scheme = index_parts.scheme + self._session = session + + def request(self, host, handler, request_body, verbose=False): + parts = (self._scheme, host, handler, None, None, None) + url = urllib_parse.urlunparse(parts) + try: + headers = {'Content-Type': 'text/xml'} + response = self._session.post(url, data=request_body, + headers=headers, stream=True) + response.raise_for_status() + self.verbose = verbose + return self.parse_response(response.raw) + except requests.HTTPError as exc: + logger.critical( + "HTTP error %s while getting %s", + exc.response.status_code, url, + ) + raise + + +def unpack_url(link, location, download_dir=None, + only_download=False, session=None, hashes=None, + progress_bar="on"): + """Unpack link. + If link is a VCS link: + if only_download, export into download_dir and ignore location + else unpack into location + for other types of link: + - unpack into location + - if download_dir, copy the file into download_dir + - if only_download, mark location for deletion + + :param hashes: A Hashes object, one of whose embedded hashes must match, + or HashMismatch will be raised. If the Hashes is empty, no matches are + required, and unhashable types of requirements (like VCS ones, which + would ordinarily raise HashUnsupported) are allowed. + """ + # non-editable vcs urls + if is_vcs_url(link): + unpack_vcs_link(link, location) + + # file urls + elif is_file_url(link): + unpack_file_url(link, location, download_dir, hashes=hashes) + + # http urls + else: + if session is None: + session = PipSession() + + unpack_http_url( + link, + location, + download_dir, + session, + hashes=hashes, + progress_bar=progress_bar + ) + if only_download: + write_delete_marker_file(location) + + +def _download_http_url(link, session, temp_dir, hashes, progress_bar): + """Download link url into temp_dir using provided session""" + target_url = link.url.split('#', 1)[0] + try: + resp = session.get( + target_url, + # We use Accept-Encoding: identity here because requests + # defaults to accepting compressed responses. This breaks in + # a variety of ways depending on how the server is configured. + # - Some servers will notice that the file isn't a compressible + # file and will leave the file alone and with an empty + # Content-Encoding + # - Some servers will notice that the file is already + # compressed and will leave the file alone and will add a + # Content-Encoding: gzip header + # - Some servers won't notice anything at all and will take + # a file that's already been compressed and compress it again + # and set the Content-Encoding: gzip header + # By setting this to request only the identity encoding We're + # hoping to eliminate the third case. Hopefully there does not + # exist a server which when given a file will notice it is + # already compressed and that you're not asking for a + # compressed file and will then decompress it before sending + # because if that's the case I don't think it'll ever be + # possible to make this work. + headers={"Accept-Encoding": "identity"}, + stream=True, + ) + resp.raise_for_status() + except requests.HTTPError as exc: + logger.critical( + "HTTP error %s while getting %s", exc.response.status_code, link, + ) + raise + + content_type = resp.headers.get('content-type', '') + filename = link.filename # fallback + # Have a look at the Content-Disposition header for a better guess + content_disposition = resp.headers.get('content-disposition') + if content_disposition: + type, params = cgi.parse_header(content_disposition) + # We use ``or`` here because we don't want to use an "empty" value + # from the filename param. + filename = params.get('filename') or filename + ext = splitext(filename)[1] + if not ext: + ext = mimetypes.guess_extension(content_type) + if ext: + filename += ext + if not ext and link.url != resp.url: + ext = os.path.splitext(resp.url)[1] + if ext: + filename += ext + file_path = os.path.join(temp_dir, filename) + with open(file_path, 'wb') as content_file: + _download_url(resp, link, content_file, hashes, progress_bar) + return file_path, content_type + + +def _check_download_dir(link, download_dir, hashes): + """ Check download_dir for previously downloaded file with correct hash + If a correct file is found return its path else None + """ + download_path = os.path.join(download_dir, link.filename) + if os.path.exists(download_path): + # If already downloaded, does its hash match? + logger.info('File was already downloaded %s', download_path) + if hashes: + try: + hashes.check_against_path(download_path) + except HashMismatch: + logger.warning( + 'Previously-downloaded file %s has bad hash. ' + 'Re-downloading.', + download_path + ) + os.unlink(download_path) + return None + return download_path + return None diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/exceptions.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/exceptions.py new file mode 100644 index 0000000..f1ca6f3 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/exceptions.py @@ -0,0 +1,268 @@ +"""Exceptions used throughout package""" +from __future__ import absolute_import + +from itertools import chain, groupby, repeat + +from pip._vendor.six import iteritems + + +class PipError(Exception): + """Base pip exception""" + + +class ConfigurationError(PipError): + """General exception in configuration""" + + +class InstallationError(PipError): + """General exception during installation""" + + +class UninstallationError(PipError): + """General exception during uninstallation""" + + +class DistributionNotFound(InstallationError): + """Raised when a distribution cannot be found to satisfy a requirement""" + + +class RequirementsFileParseError(InstallationError): + """Raised when a general error occurs parsing a requirements file line.""" + + +class BestVersionAlreadyInstalled(PipError): + """Raised when the most up-to-date version of a package is already + installed.""" + + +class BadCommand(PipError): + """Raised when virtualenv or a command is not found""" + + +class CommandError(PipError): + """Raised when there is an error in command-line arguments""" + + +class PreviousBuildDirError(PipError): + """Raised when there's a previous conflicting build directory""" + + +class InvalidWheelFilename(InstallationError): + """Invalid wheel filename.""" + + +class UnsupportedWheel(InstallationError): + """Unsupported wheel.""" + + +class HashErrors(InstallationError): + """Multiple HashError instances rolled into one for reporting""" + + def __init__(self): + self.errors = [] + + def append(self, error): + self.errors.append(error) + + def __str__(self): + lines = [] + self.errors.sort(key=lambda e: e.order) + for cls, errors_of_cls in groupby(self.errors, lambda e: e.__class__): + lines.append(cls.head) + lines.extend(e.body() for e in errors_of_cls) + if lines: + return '\n'.join(lines) + + def __nonzero__(self): + return bool(self.errors) + + def __bool__(self): + return self.__nonzero__() + + +class HashError(InstallationError): + """ + A failure to verify a package against known-good hashes + + :cvar order: An int sorting hash exception classes by difficulty of + recovery (lower being harder), so the user doesn't bother fretting + about unpinned packages when he has deeper issues, like VCS + dependencies, to deal with. Also keeps error reports in a + deterministic order. + :cvar head: A section heading for display above potentially many + exceptions of this kind + :ivar req: The InstallRequirement that triggered this error. This is + pasted on after the exception is instantiated, because it's not + typically available earlier. + + """ + req = None + head = '' + + def body(self): + """Return a summary of me for display under the heading. + + This default implementation simply prints a description of the + triggering requirement. + + :param req: The InstallRequirement that provoked this error, with + populate_link() having already been called + + """ + return ' %s' % self._requirement_name() + + def __str__(self): + return '%s\n%s' % (self.head, self.body()) + + def _requirement_name(self): + """Return a description of the requirement that triggered me. + + This default implementation returns long description of the req, with + line numbers + + """ + return str(self.req) if self.req else 'unknown package' + + +class VcsHashUnsupported(HashError): + """A hash was provided for a version-control-system-based requirement, but + we don't have a method for hashing those.""" + + order = 0 + head = ("Can't verify hashes for these requirements because we don't " + "have a way to hash version control repositories:") + + +class DirectoryUrlHashUnsupported(HashError): + """A hash was provided for a version-control-system-based requirement, but + we don't have a method for hashing those.""" + + order = 1 + head = ("Can't verify hashes for these file:// requirements because they " + "point to directories:") + + +class HashMissing(HashError): + """A hash was needed for a requirement but is absent.""" + + order = 2 + head = ('Hashes are required in --require-hashes mode, but they are ' + 'missing from some requirements. Here is a list of those ' + 'requirements along with the hashes their downloaded archives ' + 'actually had. Add lines like these to your requirements files to ' + 'prevent tampering. (If you did not enable --require-hashes ' + 'manually, note that it turns on automatically when any package ' + 'has a hash.)') + + def __init__(self, gotten_hash): + """ + :param gotten_hash: The hash of the (possibly malicious) archive we + just downloaded + """ + self.gotten_hash = gotten_hash + + def body(self): + # Dodge circular import. + from pip._internal.utils.hashes import FAVORITE_HASH + + package = None + if self.req: + # In the case of URL-based requirements, display the original URL + # seen in the requirements file rather than the package name, + # so the output can be directly copied into the requirements file. + package = (self.req.original_link if self.req.original_link + # In case someone feeds something downright stupid + # to InstallRequirement's constructor. + else getattr(self.req, 'req', None)) + return ' %s --hash=%s:%s' % (package or 'unknown package', + FAVORITE_HASH, + self.gotten_hash) + + +class HashUnpinned(HashError): + """A requirement had a hash specified but was not pinned to a specific + version.""" + + order = 3 + head = ('In --require-hashes mode, all requirements must have their ' + 'versions pinned with ==. These do not:') + + +class HashMismatch(HashError): + """ + Distribution file hash values don't match. + + :ivar package_name: The name of the package that triggered the hash + mismatch. Feel free to write to this after the exception is raise to + improve its error message. + + """ + order = 4 + head = ('THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS ' + 'FILE. If you have updated the package versions, please update ' + 'the hashes. Otherwise, examine the package contents carefully; ' + 'someone may have tampered with them.') + + def __init__(self, allowed, gots): + """ + :param allowed: A dict of algorithm names pointing to lists of allowed + hex digests + :param gots: A dict of algorithm names pointing to hashes we + actually got from the files under suspicion + """ + self.allowed = allowed + self.gots = gots + + def body(self): + return ' %s:\n%s' % (self._requirement_name(), + self._hash_comparison()) + + def _hash_comparison(self): + """ + Return a comparison of actual and expected hash values. + + Example:: + + Expected sha256 abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde + or 123451234512345123451234512345123451234512345 + Got bcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdef + + """ + def hash_then_or(hash_name): + # For now, all the decent hashes have 6-char names, so we can get + # away with hard-coding space literals. + return chain([hash_name], repeat(' or')) + + lines = [] + for hash_name, expecteds in iteritems(self.allowed): + prefix = hash_then_or(hash_name) + lines.extend((' Expected %s %s' % (next(prefix), e)) + for e in expecteds) + lines.append(' Got %s\n' % + self.gots[hash_name].hexdigest()) + prefix = ' or' + return '\n'.join(lines) + + +class UnsupportedPythonVersion(InstallationError): + """Unsupported python version according to Requires-Python package + metadata.""" + + +class ConfigurationFileCouldNotBeLoaded(ConfigurationError): + """When there are errors while loading a configuration file + """ + + def __init__(self, reason="could not be loaded", fname=None, error=None): + super(ConfigurationFileCouldNotBeLoaded, self).__init__(error) + self.reason = reason + self.fname = fname + self.error = error + + def __str__(self): + if self.fname is not None: + message_part = " in {}.".format(self.fname) + else: + assert self.error is not None + message_part = ".\n{}\n".format(self.error.message) + return "Configuration file {}{}".format(self.reason, message_part) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/index.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/index.py new file mode 100644 index 0000000..c3861c0 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/index.py @@ -0,0 +1,899 @@ +"""Routines related to PyPI, indexes""" +from __future__ import absolute_import + +import cgi +import itertools +import logging +import mimetypes +import os +import posixpath +import re +import sys +from collections import namedtuple + +from pip._vendor import html5lib, requests, six +from pip._vendor.distlib.compat import unescape +from pip._vendor.packaging import specifiers +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.requests.exceptions import HTTPError, SSLError +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip._internal.download import HAS_TLS, is_url, path_to_url, url_to_path +from pip._internal.exceptions import ( + BestVersionAlreadyInstalled, DistributionNotFound, InvalidWheelFilename, + UnsupportedWheel, +) +from pip._internal.models.candidate import InstallationCandidate +from pip._internal.models.format_control import FormatControl +from pip._internal.models.index import PyPI +from pip._internal.models.link import Link +from pip._internal.pep425tags import get_supported +from pip._internal.utils.compat import ipaddress +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + ARCHIVE_EXTENSIONS, SUPPORTED_EXTENSIONS, normalize_path, + remove_auth_from_url, +) +from pip._internal.utils.packaging import check_requires_python +from pip._internal.wheel import Wheel, wheel_ext + +__all__ = ['FormatControl', 'PackageFinder'] + + +SECURE_ORIGINS = [ + # protocol, hostname, port + # Taken from Chrome's list of secure origins (See: http://bit.ly/1qrySKC) + ("https", "*", "*"), + ("*", "localhost", "*"), + ("*", "127.0.0.0/8", "*"), + ("*", "::1/128", "*"), + ("file", "*", None), + # ssh is always secure. + ("ssh", "*", "*"), +] + + +logger = logging.getLogger(__name__) + + +def _get_content_type(url, session): + """Get the Content-Type of the given url, using a HEAD request""" + scheme, netloc, path, query, fragment = urllib_parse.urlsplit(url) + if scheme not in {'http', 'https'}: + # FIXME: some warning or something? + # assertion error? + return '' + + resp = session.head(url, allow_redirects=True) + resp.raise_for_status() + + return resp.headers.get("Content-Type", "") + + +def _handle_get_page_fail(link, reason, url, meth=None): + if meth is None: + meth = logger.debug + meth("Could not fetch URL %s: %s - skipping", link, reason) + + +def _get_html_page(link, session=None): + if session is None: + raise TypeError( + "_get_html_page() missing 1 required keyword argument: 'session'" + ) + + url = link.url + url = url.split('#', 1)[0] + + # Check for VCS schemes that do not support lookup as web pages. + from pip._internal.vcs import VcsSupport + for scheme in VcsSupport.schemes: + if url.lower().startswith(scheme) and url[len(scheme)] in '+:': + logger.debug('Cannot look at %s URL %s', scheme, link) + return None + + try: + filename = link.filename + for bad_ext in ARCHIVE_EXTENSIONS: + if filename.endswith(bad_ext): + content_type = _get_content_type(url, session=session) + if content_type.lower().startswith('text/html'): + break + else: + logger.debug( + 'Skipping page %s because of Content-Type: %s', + link, + content_type, + ) + return + + logger.debug('Getting page %s', url) + + # Tack index.html onto file:// URLs that point to directories + (scheme, netloc, path, params, query, fragment) = \ + urllib_parse.urlparse(url) + if (scheme == 'file' and + os.path.isdir(urllib_request.url2pathname(path))): + # add trailing slash if not present so urljoin doesn't trim + # final segment + if not url.endswith('/'): + url += '/' + url = urllib_parse.urljoin(url, 'index.html') + logger.debug(' file: URL is directory, getting %s', url) + + resp = session.get( + url, + headers={ + "Accept": "text/html", + # We don't want to blindly returned cached data for + # /simple/, because authors generally expecting that + # twine upload && pip install will function, but if + # they've done a pip install in the last ~10 minutes + # it won't. Thus by setting this to zero we will not + # blindly use any cached data, however the benefit of + # using max-age=0 instead of no-cache, is that we will + # still support conditional requests, so we will still + # minimize traffic sent in cases where the page hasn't + # changed at all, we will just always incur the round + # trip for the conditional GET now instead of only + # once per 10 minutes. + # For more information, please see pypa/pip#5670. + "Cache-Control": "max-age=0", + }, + ) + resp.raise_for_status() + + # The check for archives above only works if the url ends with + # something that looks like an archive. However that is not a + # requirement of an url. Unless we issue a HEAD request on every + # url we cannot know ahead of time for sure if something is HTML + # or not. However we can check after we've downloaded it. + content_type = resp.headers.get('Content-Type', 'unknown') + if not content_type.lower().startswith("text/html"): + logger.debug( + 'Skipping page %s because of Content-Type: %s', + link, + content_type, + ) + return + + inst = HTMLPage(resp.content, resp.url, resp.headers) + except HTTPError as exc: + _handle_get_page_fail(link, exc, url) + except SSLError as exc: + reason = "There was a problem confirming the ssl certificate: " + reason += str(exc) + _handle_get_page_fail(link, reason, url, meth=logger.info) + except requests.ConnectionError as exc: + _handle_get_page_fail(link, "connection error: %s" % exc, url) + except requests.Timeout: + _handle_get_page_fail(link, "timed out", url) + else: + return inst + + +class PackageFinder(object): + """This finds packages. + + This is meant to match easy_install's technique for looking for + packages, by reading pages and looking for appropriate links. + """ + + def __init__(self, find_links, index_urls, allow_all_prereleases=False, + trusted_hosts=None, process_dependency_links=False, + session=None, format_control=None, platform=None, + versions=None, abi=None, implementation=None, + prefer_binary=False): + """Create a PackageFinder. + + :param format_control: A FormatControl object or None. Used to control + the selection of source packages / binary packages when consulting + the index and links. + :param platform: A string or None. If None, searches for packages + that are supported by the current system. Otherwise, will find + packages that can be built on the platform passed in. These + packages will only be downloaded for distribution: they will + not be built locally. + :param versions: A list of strings or None. This is passed directly + to pep425tags.py in the get_supported() method. + :param abi: A string or None. This is passed directly + to pep425tags.py in the get_supported() method. + :param implementation: A string or None. This is passed directly + to pep425tags.py in the get_supported() method. + """ + if session is None: + raise TypeError( + "PackageFinder() missing 1 required keyword argument: " + "'session'" + ) + + # Build find_links. If an argument starts with ~, it may be + # a local file relative to a home directory. So try normalizing + # it and if it exists, use the normalized version. + # This is deliberately conservative - it might be fine just to + # blindly normalize anything starting with a ~... + self.find_links = [] + for link in find_links: + if link.startswith('~'): + new_link = normalize_path(link) + if os.path.exists(new_link): + link = new_link + self.find_links.append(link) + + self.index_urls = index_urls + self.dependency_links = [] + + # These are boring links that have already been logged somehow: + self.logged_links = set() + + self.format_control = format_control or FormatControl(set(), set()) + + # Domains that we won't emit warnings for when not using HTTPS + self.secure_origins = [ + ("*", host, "*") + for host in (trusted_hosts if trusted_hosts else []) + ] + + # Do we want to allow _all_ pre-releases? + self.allow_all_prereleases = allow_all_prereleases + + # Do we process dependency links? + self.process_dependency_links = process_dependency_links + + # The Session we'll use to make requests + self.session = session + + # The valid tags to check potential found wheel candidates against + self.valid_tags = get_supported( + versions=versions, + platform=platform, + abi=abi, + impl=implementation, + ) + + # Do we prefer old, but valid, binary dist over new source dist + self.prefer_binary = prefer_binary + + # If we don't have TLS enabled, then WARN if anyplace we're looking + # relies on TLS. + if not HAS_TLS: + for link in itertools.chain(self.index_urls, self.find_links): + parsed = urllib_parse.urlparse(link) + if parsed.scheme == "https": + logger.warning( + "pip is configured with locations that require " + "TLS/SSL, however the ssl module in Python is not " + "available." + ) + break + + def get_formatted_locations(self): + lines = [] + if self.index_urls and self.index_urls != [PyPI.simple_url]: + lines.append( + "Looking in indexes: {}".format(", ".join( + remove_auth_from_url(url) for url in self.index_urls)) + ) + if self.find_links: + lines.append( + "Looking in links: {}".format(", ".join(self.find_links)) + ) + return "\n".join(lines) + + def add_dependency_links(self, links): + # FIXME: this shouldn't be global list this, it should only + # apply to requirements of the package that specifies the + # dependency_links value + # FIXME: also, we should track comes_from (i.e., use Link) + if self.process_dependency_links: + deprecated( + "Dependency Links processing has been deprecated and will be " + "removed in a future release.", + replacement="PEP 508 URL dependencies", + gone_in="18.2", + issue=4187, + ) + self.dependency_links.extend(links) + + @staticmethod + def _sort_locations(locations, expand_dir=False): + """ + Sort locations into "files" (archives) and "urls", and return + a pair of lists (files,urls) + """ + files = [] + urls = [] + + # puts the url for the given file path into the appropriate list + def sort_path(path): + url = path_to_url(path) + if mimetypes.guess_type(url, strict=False)[0] == 'text/html': + urls.append(url) + else: + files.append(url) + + for url in locations: + + is_local_path = os.path.exists(url) + is_file_url = url.startswith('file:') + + if is_local_path or is_file_url: + if is_local_path: + path = url + else: + path = url_to_path(url) + if os.path.isdir(path): + if expand_dir: + path = os.path.realpath(path) + for item in os.listdir(path): + sort_path(os.path.join(path, item)) + elif is_file_url: + urls.append(url) + elif os.path.isfile(path): + sort_path(path) + else: + logger.warning( + "Url '%s' is ignored: it is neither a file " + "nor a directory.", url, + ) + elif is_url(url): + # Only add url with clear scheme + urls.append(url) + else: + logger.warning( + "Url '%s' is ignored. It is either a non-existing " + "path or lacks a specific scheme.", url, + ) + + return files, urls + + def _candidate_sort_key(self, candidate): + """ + Function used to generate link sort key for link tuples. + The greater the return value, the more preferred it is. + If not finding wheels, then sorted by version only. + If finding wheels, then the sort order is by version, then: + 1. existing installs + 2. wheels ordered via Wheel.support_index_min(self.valid_tags) + 3. source archives + If prefer_binary was set, then all wheels are sorted above sources. + Note: it was considered to embed this logic into the Link + comparison operators, but then different sdist links + with the same version, would have to be considered equal + """ + support_num = len(self.valid_tags) + build_tag = tuple() + binary_preference = 0 + if candidate.location.is_wheel: + # can raise InvalidWheelFilename + wheel = Wheel(candidate.location.filename) + if not wheel.supported(self.valid_tags): + raise UnsupportedWheel( + "%s is not a supported wheel for this platform. It " + "can't be sorted." % wheel.filename + ) + if self.prefer_binary: + binary_preference = 1 + pri = -(wheel.support_index_min(self.valid_tags)) + if wheel.build_tag is not None: + match = re.match(r'^(\d+)(.*)$', wheel.build_tag) + build_tag_groups = match.groups() + build_tag = (int(build_tag_groups[0]), build_tag_groups[1]) + else: # sdist + pri = -(support_num) + return (binary_preference, candidate.version, build_tag, pri) + + def _validate_secure_origin(self, logger, location): + # Determine if this url used a secure transport mechanism + parsed = urllib_parse.urlparse(str(location)) + origin = (parsed.scheme, parsed.hostname, parsed.port) + + # The protocol to use to see if the protocol matches. + # Don't count the repository type as part of the protocol: in + # cases such as "git+ssh", only use "ssh". (I.e., Only verify against + # the last scheme.) + protocol = origin[0].rsplit('+', 1)[-1] + + # Determine if our origin is a secure origin by looking through our + # hardcoded list of secure origins, as well as any additional ones + # configured on this PackageFinder instance. + for secure_origin in (SECURE_ORIGINS + self.secure_origins): + if protocol != secure_origin[0] and secure_origin[0] != "*": + continue + + try: + # We need to do this decode dance to ensure that we have a + # unicode object, even on Python 2.x. + addr = ipaddress.ip_address( + origin[1] + if ( + isinstance(origin[1], six.text_type) or + origin[1] is None + ) + else origin[1].decode("utf8") + ) + network = ipaddress.ip_network( + secure_origin[1] + if isinstance(secure_origin[1], six.text_type) + else secure_origin[1].decode("utf8") + ) + except ValueError: + # We don't have both a valid address or a valid network, so + # we'll check this origin against hostnames. + if (origin[1] and + origin[1].lower() != secure_origin[1].lower() and + secure_origin[1] != "*"): + continue + else: + # We have a valid address and network, so see if the address + # is contained within the network. + if addr not in network: + continue + + # Check to see if the port patches + if (origin[2] != secure_origin[2] and + secure_origin[2] != "*" and + secure_origin[2] is not None): + continue + + # If we've gotten here, then this origin matches the current + # secure origin and we should return True + return True + + # If we've gotten to this point, then the origin isn't secure and we + # will not accept it as a valid location to search. We will however + # log a warning that we are ignoring it. + logger.warning( + "The repository located at %s is not a trusted or secure host and " + "is being ignored. If this repository is available via HTTPS we " + "recommend you use HTTPS instead, otherwise you may silence " + "this warning and allow it anyway with '--trusted-host %s'.", + parsed.hostname, + parsed.hostname, + ) + + return False + + def _get_index_urls_locations(self, project_name): + """Returns the locations found via self.index_urls + + Checks the url_name on the main (first in the list) index and + use this url_name to produce all locations + """ + + def mkurl_pypi_url(url): + loc = posixpath.join( + url, + urllib_parse.quote(canonicalize_name(project_name))) + # For maximum compatibility with easy_install, ensure the path + # ends in a trailing slash. Although this isn't in the spec + # (and PyPI can handle it without the slash) some other index + # implementations might break if they relied on easy_install's + # behavior. + if not loc.endswith('/'): + loc = loc + '/' + return loc + + return [mkurl_pypi_url(url) for url in self.index_urls] + + def find_all_candidates(self, project_name): + """Find all available InstallationCandidate for project_name + + This checks index_urls, find_links and dependency_links. + All versions found are returned as an InstallationCandidate list. + + See _link_package_versions for details on which files are accepted + """ + index_locations = self._get_index_urls_locations(project_name) + index_file_loc, index_url_loc = self._sort_locations(index_locations) + fl_file_loc, fl_url_loc = self._sort_locations( + self.find_links, expand_dir=True, + ) + dep_file_loc, dep_url_loc = self._sort_locations(self.dependency_links) + + file_locations = (Link(url) for url in itertools.chain( + index_file_loc, fl_file_loc, dep_file_loc, + )) + + # We trust every url that the user has given us whether it was given + # via --index-url or --find-links + # We explicitly do not trust links that came from dependency_links + # We want to filter out any thing which does not have a secure origin. + url_locations = [ + link for link in itertools.chain( + (Link(url) for url in index_url_loc), + (Link(url) for url in fl_url_loc), + (Link(url) for url in dep_url_loc), + ) + if self._validate_secure_origin(logger, link) + ] + + logger.debug('%d location(s) to search for versions of %s:', + len(url_locations), project_name) + + for location in url_locations: + logger.debug('* %s', location) + + canonical_name = canonicalize_name(project_name) + formats = self.format_control.get_allowed_formats(canonical_name) + search = Search(project_name, canonical_name, formats) + find_links_versions = self._package_versions( + # We trust every directly linked archive in find_links + (Link(url, '-f') for url in self.find_links), + search + ) + + page_versions = [] + for page in self._get_pages(url_locations, project_name): + logger.debug('Analyzing links from page %s', page.url) + with indent_log(): + page_versions.extend( + self._package_versions(page.iter_links(), search) + ) + + dependency_versions = self._package_versions( + (Link(url) for url in self.dependency_links), search + ) + if dependency_versions: + logger.debug( + 'dependency_links found: %s', + ', '.join([ + version.location.url for version in dependency_versions + ]) + ) + + file_versions = self._package_versions(file_locations, search) + if file_versions: + file_versions.sort(reverse=True) + logger.debug( + 'Local files found: %s', + ', '.join([ + url_to_path(candidate.location.url) + for candidate in file_versions + ]) + ) + + # This is an intentional priority ordering + return ( + file_versions + find_links_versions + page_versions + + dependency_versions + ) + + def find_requirement(self, req, upgrade): + """Try to find a Link matching req + + Expects req, an InstallRequirement and upgrade, a boolean + Returns a Link if found, + Raises DistributionNotFound or BestVersionAlreadyInstalled otherwise + """ + all_candidates = self.find_all_candidates(req.name) + + # Filter out anything which doesn't match our specifier + compatible_versions = set( + req.specifier.filter( + # We turn the version object into a str here because otherwise + # when we're debundled but setuptools isn't, Python will see + # packaging.version.Version and + # pkg_resources._vendor.packaging.version.Version as different + # types. This way we'll use a str as a common data interchange + # format. If we stop using the pkg_resources provided specifier + # and start using our own, we can drop the cast to str(). + [str(c.version) for c in all_candidates], + prereleases=( + self.allow_all_prereleases + if self.allow_all_prereleases else None + ), + ) + ) + applicable_candidates = [ + # Again, converting to str to deal with debundling. + c for c in all_candidates if str(c.version) in compatible_versions + ] + + if applicable_candidates: + best_candidate = max(applicable_candidates, + key=self._candidate_sort_key) + else: + best_candidate = None + + if req.satisfied_by is not None: + installed_version = parse_version(req.satisfied_by.version) + else: + installed_version = None + + if installed_version is None and best_candidate is None: + logger.critical( + 'Could not find a version that satisfies the requirement %s ' + '(from versions: %s)', + req, + ', '.join( + sorted( + {str(c.version) for c in all_candidates}, + key=parse_version, + ) + ) + ) + + raise DistributionNotFound( + 'No matching distribution found for %s' % req + ) + + best_installed = False + if installed_version and ( + best_candidate is None or + best_candidate.version <= installed_version): + best_installed = True + + if not upgrade and installed_version is not None: + if best_installed: + logger.debug( + 'Existing installed version (%s) is most up-to-date and ' + 'satisfies requirement', + installed_version, + ) + else: + logger.debug( + 'Existing installed version (%s) satisfies requirement ' + '(most up-to-date version is %s)', + installed_version, + best_candidate.version, + ) + return None + + if best_installed: + # We have an existing version, and its the best version + logger.debug( + 'Installed version (%s) is most up-to-date (past versions: ' + '%s)', + installed_version, + ', '.join(sorted(compatible_versions, key=parse_version)) or + "none", + ) + raise BestVersionAlreadyInstalled + + logger.debug( + 'Using version %s (newest of versions: %s)', + best_candidate.version, + ', '.join(sorted(compatible_versions, key=parse_version)) + ) + return best_candidate.location + + def _get_pages(self, locations, project_name): + """ + Yields (page, page_url) from the given locations, skipping + locations that have errors. + """ + seen = set() + for location in locations: + if location in seen: + continue + seen.add(location) + + page = self._get_page(location) + if page is None: + continue + + yield page + + _py_version_re = re.compile(r'-py([123]\.?[0-9]?)$') + + def _sort_links(self, links): + """ + Returns elements of links in order, non-egg links first, egg links + second, while eliminating duplicates + """ + eggs, no_eggs = [], [] + seen = set() + for link in links: + if link not in seen: + seen.add(link) + if link.egg_fragment: + eggs.append(link) + else: + no_eggs.append(link) + return no_eggs + eggs + + def _package_versions(self, links, search): + result = [] + for link in self._sort_links(links): + v = self._link_package_versions(link, search) + if v is not None: + result.append(v) + return result + + def _log_skipped_link(self, link, reason): + if link not in self.logged_links: + logger.debug('Skipping link %s; %s', link, reason) + self.logged_links.add(link) + + def _link_package_versions(self, link, search): + """Return an InstallationCandidate or None""" + version = None + if link.egg_fragment: + egg_info = link.egg_fragment + ext = link.ext + else: + egg_info, ext = link.splitext() + if not ext: + self._log_skipped_link(link, 'not a file') + return + if ext not in SUPPORTED_EXTENSIONS: + self._log_skipped_link( + link, 'unsupported archive format: %s' % ext, + ) + return + if "binary" not in search.formats and ext == wheel_ext: + self._log_skipped_link( + link, 'No binaries permitted for %s' % search.supplied, + ) + return + if "macosx10" in link.path and ext == '.zip': + self._log_skipped_link(link, 'macosx10 one') + return + if ext == wheel_ext: + try: + wheel = Wheel(link.filename) + except InvalidWheelFilename: + self._log_skipped_link(link, 'invalid wheel filename') + return + if canonicalize_name(wheel.name) != search.canonical: + self._log_skipped_link( + link, 'wrong project name (not %s)' % search.supplied) + return + + if not wheel.supported(self.valid_tags): + self._log_skipped_link( + link, 'it is not compatible with this Python') + return + + version = wheel.version + + # This should be up by the search.ok_binary check, but see issue 2700. + if "source" not in search.formats and ext != wheel_ext: + self._log_skipped_link( + link, 'No sources permitted for %s' % search.supplied, + ) + return + + if not version: + version = egg_info_matches(egg_info, search.supplied, link) + if version is None: + self._log_skipped_link( + link, 'Missing project version for %s' % search.supplied) + return + + match = self._py_version_re.search(version) + if match: + version = version[:match.start()] + py_version = match.group(1) + if py_version != sys.version[:3]: + self._log_skipped_link( + link, 'Python version is incorrect') + return + try: + support_this_python = check_requires_python(link.requires_python) + except specifiers.InvalidSpecifier: + logger.debug("Package %s has an invalid Requires-Python entry: %s", + link.filename, link.requires_python) + support_this_python = True + + if not support_this_python: + logger.debug("The package %s is incompatible with the python" + "version in use. Acceptable python versions are:%s", + link, link.requires_python) + return + logger.debug('Found link %s, version: %s', link, version) + + return InstallationCandidate(search.supplied, version, link) + + def _get_page(self, link): + return _get_html_page(link, session=self.session) + + +def egg_info_matches( + egg_info, search_name, link, + _egg_info_re=re.compile(r'([a-z0-9_.]+)-([a-z0-9_.!+-]+)', re.I)): + """Pull the version part out of a string. + + :param egg_info: The string to parse. E.g. foo-2.1 + :param search_name: The name of the package this belongs to. None to + infer the name. Note that this cannot unambiguously parse strings + like foo-2-2 which might be foo, 2-2 or foo-2, 2. + :param link: The link the string came from, for logging on failure. + """ + match = _egg_info_re.search(egg_info) + if not match: + logger.debug('Could not parse version from link: %s', link) + return None + if search_name is None: + full_match = match.group(0) + return full_match.split('-', 1)[-1] + name = match.group(0).lower() + # To match the "safe" name that pkg_resources creates: + name = name.replace('_', '-') + # project name and version must be separated by a dash + look_for = search_name.lower() + "-" + if name.startswith(look_for): + return match.group(0)[len(look_for):] + else: + return None + + +def _determine_base_url(document, page_url): + """Determine the HTML document's base URL. + + This looks for a ```` tag in the HTML document. If present, its href + attribute denotes the base URL of anchor tags in the document. If there is + no such tag (or if it does not have a valid href attribute), the HTML + file's URL is used as the base URL. + + :param document: An HTML document representation. The current + implementation expects the result of ``html5lib.parse()``. + :param page_url: The URL of the HTML document. + """ + for base in document.findall(".//base"): + href = base.get("href") + if href is not None: + return href + return page_url + + +def _get_encoding_from_headers(headers): + """Determine if we have any encoding information in our headers. + """ + if headers and "Content-Type" in headers: + content_type, params = cgi.parse_header(headers["Content-Type"]) + if "charset" in params: + return params['charset'] + return None + + +_CLEAN_LINK_RE = re.compile(r'[^a-z0-9$&+,/:;=?@.#%_\\|-]', re.I) + + +def _clean_link(url): + """Makes sure a link is fully encoded. That is, if a ' ' shows up in + the link, it will be rewritten to %20 (while not over-quoting + % or other characters).""" + return _CLEAN_LINK_RE.sub(lambda match: '%%%2x' % ord(match.group(0)), url) + + +class HTMLPage(object): + """Represents one page, along with its URL""" + + def __init__(self, content, url, headers=None): + self.content = content + self.url = url + self.headers = headers + + def __str__(self): + return self.url + + def iter_links(self): + """Yields all links in the page""" + document = html5lib.parse( + self.content, + transport_encoding=_get_encoding_from_headers(self.headers), + namespaceHTMLElements=False, + ) + base_url = _determine_base_url(document, self.url) + for anchor in document.findall(".//a"): + if anchor.get("href"): + href = anchor.get("href") + url = _clean_link(urllib_parse.urljoin(base_url, href)) + pyrequire = anchor.get('data-requires-python') + pyrequire = unescape(pyrequire) if pyrequire else None + yield Link(url, self.url, requires_python=pyrequire) + + +Search = namedtuple('Search', 'supplied canonical formats') +"""Capture key aspects of a search. + +:attribute supplied: The user supplied package. +:attribute canonical: The canonical package name. +:attribute formats: The formats allowed for this package. Should be a set + with 'binary' or 'source' or both in it. +""" diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/locations.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/locations.py new file mode 100644 index 0000000..183aaa3 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/locations.py @@ -0,0 +1,194 @@ +"""Locations where we look for configs, install stuff, etc""" +from __future__ import absolute_import + +import os +import os.path +import platform +import site +import sys +import sysconfig +from distutils import sysconfig as distutils_sysconfig +from distutils.command.install import SCHEME_KEYS # type: ignore + +from pip._internal.utils import appdirs +from pip._internal.utils.compat import WINDOWS, expanduser + +# Application Directories +USER_CACHE_DIR = appdirs.user_cache_dir("pip") + + +DELETE_MARKER_MESSAGE = '''\ +This file is placed here by pip to indicate the source was put +here by pip. + +Once this package is successfully installed this source code will be +deleted (unless you remove this file). +''' +PIP_DELETE_MARKER_FILENAME = 'pip-delete-this-directory.txt' + + +def write_delete_marker_file(directory): + """ + Write the pip delete marker file into this directory. + """ + filepath = os.path.join(directory, PIP_DELETE_MARKER_FILENAME) + with open(filepath, 'w') as marker_fp: + marker_fp.write(DELETE_MARKER_MESSAGE) + + +def running_under_virtualenv(): + """ + Return True if we're running inside a virtualenv, False otherwise. + + """ + if hasattr(sys, 'real_prefix'): + return True + elif sys.prefix != getattr(sys, "base_prefix", sys.prefix): + return True + + return False + + +def virtualenv_no_global(): + """ + Return True if in a venv and no system site packages. + """ + # this mirrors the logic in virtualenv.py for locating the + # no-global-site-packages.txt file + site_mod_dir = os.path.dirname(os.path.abspath(site.__file__)) + no_global_file = os.path.join(site_mod_dir, 'no-global-site-packages.txt') + if running_under_virtualenv() and os.path.isfile(no_global_file): + return True + + +if running_under_virtualenv(): + src_prefix = os.path.join(sys.prefix, 'src') +else: + # FIXME: keep src in cwd for now (it is not a temporary folder) + try: + src_prefix = os.path.join(os.getcwd(), 'src') + except OSError: + # In case the current working directory has been renamed or deleted + sys.exit( + "The folder you are executing pip from can no longer be found." + ) + +# under macOS + virtualenv sys.prefix is not properly resolved +# it is something like /path/to/python/bin/.. +# Note: using realpath due to tmp dirs on OSX being symlinks +src_prefix = os.path.abspath(src_prefix) + +# FIXME doesn't account for venv linked to global site-packages + +site_packages = sysconfig.get_path("purelib") +# This is because of a bug in PyPy's sysconfig module, see +# https://bitbucket.org/pypy/pypy/issues/2506/sysconfig-returns-incorrect-paths +# for more information. +if platform.python_implementation().lower() == "pypy": + site_packages = distutils_sysconfig.get_python_lib() +try: + # Use getusersitepackages if this is present, as it ensures that the + # value is initialised properly. + user_site = site.getusersitepackages() +except AttributeError: + user_site = site.USER_SITE +user_dir = expanduser('~') +if WINDOWS: + bin_py = os.path.join(sys.prefix, 'Scripts') + bin_user = os.path.join(user_site, 'Scripts') + # buildout uses 'bin' on Windows too? + if not os.path.exists(bin_py): + bin_py = os.path.join(sys.prefix, 'bin') + bin_user = os.path.join(user_site, 'bin') + + config_basename = 'pip.ini' + + legacy_storage_dir = os.path.join(user_dir, 'pip') + legacy_config_file = os.path.join( + legacy_storage_dir, + config_basename, + ) +else: + bin_py = os.path.join(sys.prefix, 'bin') + bin_user = os.path.join(user_site, 'bin') + + config_basename = 'pip.conf' + + legacy_storage_dir = os.path.join(user_dir, '.pip') + legacy_config_file = os.path.join( + legacy_storage_dir, + config_basename, + ) + # Forcing to use /usr/local/bin for standard macOS framework installs + # Also log to ~/Library/Logs/ for use with the Console.app log viewer + if sys.platform[:6] == 'darwin' and sys.prefix[:16] == '/System/Library/': + bin_py = '/usr/local/bin' + +site_config_files = [ + os.path.join(path, config_basename) + for path in appdirs.site_config_dirs('pip') +] + +venv_config_file = os.path.join(sys.prefix, config_basename) +new_config_file = os.path.join(appdirs.user_config_dir("pip"), config_basename) + + +def distutils_scheme(dist_name, user=False, home=None, root=None, + isolated=False, prefix=None): + """ + Return a distutils install scheme + """ + from distutils.dist import Distribution + + scheme = {} + + if isolated: + extra_dist_args = {"script_args": ["--no-user-cfg"]} + else: + extra_dist_args = {} + dist_args = {'name': dist_name} + dist_args.update(extra_dist_args) + + d = Distribution(dist_args) + d.parse_config_files() + i = d.get_command_obj('install', create=True) + # NOTE: setting user or home has the side-effect of creating the home dir + # or user base for installations during finalize_options() + # ideally, we'd prefer a scheme class that has no side-effects. + assert not (user and prefix), "user={} prefix={}".format(user, prefix) + i.user = user or i.user + if user: + i.prefix = "" + i.prefix = prefix or i.prefix + i.home = home or i.home + i.root = root or i.root + i.finalize_options() + for key in SCHEME_KEYS: + scheme[key] = getattr(i, 'install_' + key) + + # install_lib specified in setup.cfg should install *everything* + # into there (i.e. it takes precedence over both purelib and + # platlib). Note, i.install_lib is *always* set after + # finalize_options(); we only want to override here if the user + # has explicitly requested it hence going back to the config + if 'install_lib' in d.get_option_dict('install'): + scheme.update(dict(purelib=i.install_lib, platlib=i.install_lib)) + + if running_under_virtualenv(): + scheme['headers'] = os.path.join( + sys.prefix, + 'include', + 'site', + 'python' + sys.version[:3], + dist_name, + ) + + if root is not None: + path_no_drive = os.path.splitdrive( + os.path.abspath(scheme["headers"]))[1] + scheme["headers"] = os.path.join( + root, + path_no_drive[1:], + ) + + return scheme diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/models/__init__.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/models/__init__.py new file mode 100644 index 0000000..7855226 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/models/__init__.py @@ -0,0 +1,2 @@ +"""A package that contains models that represent entities. +""" diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/models/candidate.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/models/candidate.py new file mode 100644 index 0000000..c736de6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/models/candidate.py @@ -0,0 +1,23 @@ +from pip._vendor.packaging.version import parse as parse_version + +from pip._internal.utils.models import KeyBasedCompareMixin + + +class InstallationCandidate(KeyBasedCompareMixin): + """Represents a potential "candidate" for installation. + """ + + def __init__(self, project, version, location): + self.project = project + self.version = parse_version(version) + self.location = location + + super(InstallationCandidate, self).__init__( + key=(self.project, self.version, self.location), + defining_class=InstallationCandidate + ) + + def __repr__(self): + return "".format( + self.project, self.version, self.location, + ) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/models/format_control.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/models/format_control.py new file mode 100644 index 0000000..2748856 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/models/format_control.py @@ -0,0 +1,62 @@ +from pip._vendor.packaging.utils import canonicalize_name + + +class FormatControl(object): + """A helper class for controlling formats from which packages are installed. + If a field is falsy, it isn't set. If it is {':all:'}, it should match all + packages except those listed in the other field. Only one field can be set + to {':all:'} at a time. The rest of the time exact package name matches + are listed, with any given package only showing up in one field at a time. + """ + def __init__(self, no_binary=None, only_binary=None): + self.no_binary = set() if no_binary is None else no_binary + self.only_binary = set() if only_binary is None else only_binary + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + return "{}({}, {})".format( + self.__class__.__name__, + self.no_binary, + self.only_binary + ) + + @staticmethod + def handle_mutual_excludes(value, target, other): + new = value.split(',') + while ':all:' in new: + other.clear() + target.clear() + target.add(':all:') + del new[:new.index(':all:') + 1] + # Without a none, we want to discard everything as :all: covers it + if ':none:' not in new: + return + for name in new: + if name == ':none:': + target.clear() + continue + name = canonicalize_name(name) + other.discard(name) + target.add(name) + + def get_allowed_formats(self, canonical_name): + result = {"binary", "source"} + if canonical_name in self.only_binary: + result.discard('source') + elif canonical_name in self.no_binary: + result.discard('binary') + elif ':all:' in self.only_binary: + result.discard('source') + elif ':all:' in self.no_binary: + result.discard('binary') + return frozenset(result) + + def disallow_binaries(self): + self.handle_mutual_excludes( + ':all:', self.no_binary, self.only_binary, + ) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/models/index.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/models/index.py new file mode 100644 index 0000000..870a315 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/models/index.py @@ -0,0 +1,29 @@ +from pip._vendor.six.moves.urllib import parse as urllib_parse + + +class PackageIndex(object): + """Represents a Package Index and provides easier access to endpoints + """ + + def __init__(self, url, file_storage_domain): + super(PackageIndex, self).__init__() + self.url = url + self.netloc = urllib_parse.urlsplit(url).netloc + self.simple_url = self._url_for_path('simple') + self.pypi_url = self._url_for_path('pypi') + + # This is part of a temporary hack used to block installs of PyPI + # packages which depend on external urls only necessary until PyPI can + # block such packages themselves + self.file_storage_domain = file_storage_domain + + def _url_for_path(self, path): + return urllib_parse.urljoin(self.url, path) + + +PyPI = PackageIndex( + 'https://pypi.org/', file_storage_domain='files.pythonhosted.org' +) +TestPyPI = PackageIndex( + 'https://test.pypi.org/', file_storage_domain='test-files.pythonhosted.org' +) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/models/link.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/models/link.py new file mode 100644 index 0000000..5decb7c --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/models/link.py @@ -0,0 +1,141 @@ +import posixpath +import re + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.download import path_to_url +from pip._internal.utils.misc import splitext +from pip._internal.utils.models import KeyBasedCompareMixin +from pip._internal.wheel import wheel_ext + + +class Link(KeyBasedCompareMixin): + """Represents a parsed link from a Package Index's simple URL + """ + + def __init__(self, url, comes_from=None, requires_python=None): + """ + url: + url of the resource pointed to (href of the link) + comes_from: + instance of HTMLPage where the link was found, or string. + requires_python: + String containing the `Requires-Python` metadata field, specified + in PEP 345. This may be specified by a data-requires-python + attribute in the HTML link tag, as described in PEP 503. + """ + + # url can be a UNC windows share + if url.startswith('\\\\'): + url = path_to_url(url) + + self.url = url + self.comes_from = comes_from + self.requires_python = requires_python if requires_python else None + + super(Link, self).__init__( + key=(self.url), + defining_class=Link + ) + + def __str__(self): + if self.requires_python: + rp = ' (requires-python:%s)' % self.requires_python + else: + rp = '' + if self.comes_from: + return '%s (from %s)%s' % (self.url, self.comes_from, rp) + else: + return str(self.url) + + def __repr__(self): + return '' % self + + @property + def filename(self): + _, netloc, path, _, _ = urllib_parse.urlsplit(self.url) + name = posixpath.basename(path.rstrip('/')) or netloc + name = urllib_parse.unquote(name) + assert name, ('URL %r produced no filename' % self.url) + return name + + @property + def scheme(self): + return urllib_parse.urlsplit(self.url)[0] + + @property + def netloc(self): + return urllib_parse.urlsplit(self.url)[1] + + @property + def path(self): + return urllib_parse.unquote(urllib_parse.urlsplit(self.url)[2]) + + def splitext(self): + return splitext(posixpath.basename(self.path.rstrip('/'))) + + @property + def ext(self): + return self.splitext()[1] + + @property + def url_without_fragment(self): + scheme, netloc, path, query, fragment = urllib_parse.urlsplit(self.url) + return urllib_parse.urlunsplit((scheme, netloc, path, query, None)) + + _egg_fragment_re = re.compile(r'[#&]egg=([^&]*)') + + @property + def egg_fragment(self): + match = self._egg_fragment_re.search(self.url) + if not match: + return None + return match.group(1) + + _subdirectory_fragment_re = re.compile(r'[#&]subdirectory=([^&]*)') + + @property + def subdirectory_fragment(self): + match = self._subdirectory_fragment_re.search(self.url) + if not match: + return None + return match.group(1) + + _hash_re = re.compile( + r'(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)' + ) + + @property + def hash(self): + match = self._hash_re.search(self.url) + if match: + return match.group(2) + return None + + @property + def hash_name(self): + match = self._hash_re.search(self.url) + if match: + return match.group(1) + return None + + @property + def show_url(self): + return posixpath.basename(self.url.split('#', 1)[0].split('?', 1)[0]) + + @property + def is_wheel(self): + return self.ext == wheel_ext + + @property + def is_artifact(self): + """ + Determines if this points to an actual artifact (e.g. a tarball) or if + it points to an "abstract" thing like a path or a VCS location. + """ + from pip._internal.vcs import vcs + + if self.scheme in vcs.all_schemes: + return False + + return True diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/operations/__init__.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/operations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/operations/check.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/operations/check.py new file mode 100644 index 0000000..799257a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/operations/check.py @@ -0,0 +1,148 @@ +"""Validation of dependencies of packages +""" + +from collections import namedtuple + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.operations.prepare import make_abstract_dist +from pip._internal.utils.misc import get_installed_distributions +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from pip._internal.req.req_install import InstallRequirement # noqa: F401 + from typing import ( # noqa: F401 + Any, Callable, Dict, Iterator, Optional, Set, Tuple, List + ) + + # Shorthands + PackageSet = Dict[str, 'PackageDetails'] + Missing = Tuple[str, Any] + Conflicting = Tuple[str, str, Any] + + MissingDict = Dict[str, List[Missing]] + ConflictingDict = Dict[str, List[Conflicting]] + CheckResult = Tuple[MissingDict, ConflictingDict] + +PackageDetails = namedtuple('PackageDetails', ['version', 'requires']) + + +def create_package_set_from_installed(**kwargs): + # type: (**Any) -> PackageSet + """Converts a list of distributions into a PackageSet. + """ + # Default to using all packages installed on the system + if kwargs == {}: + kwargs = {"local_only": False, "skip": ()} + + package_set = {} + for dist in get_installed_distributions(**kwargs): + name = canonicalize_name(dist.project_name) + package_set[name] = PackageDetails(dist.version, dist.requires()) + return package_set + + +def check_package_set(package_set, should_ignore=None): + # type: (PackageSet, Optional[Callable[[str], bool]]) -> CheckResult + """Check if a package set is consistent + + If should_ignore is passed, it should be a callable that takes a + package name and returns a boolean. + """ + if should_ignore is None: + def should_ignore(name): + return False + + missing = dict() + conflicting = dict() + + for package_name in package_set: + # Info about dependencies of package_name + missing_deps = set() # type: Set[Missing] + conflicting_deps = set() # type: Set[Conflicting] + + if should_ignore(package_name): + continue + + for req in package_set[package_name].requires: + name = canonicalize_name(req.project_name) # type: str + + # Check if it's missing + if name not in package_set: + missed = True + if req.marker is not None: + missed = req.marker.evaluate() + if missed: + missing_deps.add((name, req)) + continue + + # Check if there's a conflict + version = package_set[name].version # type: str + if not req.specifier.contains(version, prereleases=True): + conflicting_deps.add((name, version, req)) + + if missing_deps: + missing[package_name] = sorted(missing_deps, key=str) + if conflicting_deps: + conflicting[package_name] = sorted(conflicting_deps, key=str) + + return missing, conflicting + + +def check_install_conflicts(to_install): + # type: (List[InstallRequirement]) -> Tuple[PackageSet, CheckResult] + """For checking if the dependency graph would be consistent after \ + installing given requirements + """ + # Start from the current state + package_set = create_package_set_from_installed() + # Install packages + would_be_installed = _simulate_installation_of(to_install, package_set) + + # Only warn about directly-dependent packages; create a whitelist of them + whitelist = _create_whitelist(would_be_installed, package_set) + + return ( + package_set, + check_package_set( + package_set, should_ignore=lambda name: name not in whitelist + ) + ) + + +# NOTE from @pradyunsg +# This required a minor update in dependency link handling logic over at +# operations.prepare.IsSDist.dist() to get it working +def _simulate_installation_of(to_install, package_set): + # type: (List[InstallRequirement], PackageSet) -> Set[str] + """Computes the version of packages after installing to_install. + """ + + # Keep track of packages that were installed + installed = set() + + # Modify it as installing requirement_set would (assuming no errors) + for inst_req in to_install: + dist = make_abstract_dist(inst_req).dist(finder=None) + name = canonicalize_name(dist.key) + package_set[name] = PackageDetails(dist.version, dist.requires()) + + installed.add(name) + + return installed + + +def _create_whitelist(would_be_installed, package_set): + # type: (Set[str], PackageSet) -> Set[str] + packages_affected = set(would_be_installed) + + for package_name in package_set: + if package_name in packages_affected: + continue + + for req in package_set[package_name].requires: + if canonicalize_name(req.name) in packages_affected: + packages_affected.add(package_name) + break + + return packages_affected diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/operations/freeze.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/operations/freeze.py new file mode 100644 index 0000000..beb2feb --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/operations/freeze.py @@ -0,0 +1,264 @@ +from __future__ import absolute_import + +import collections +import logging +import os +import re + +from pip._vendor import pkg_resources, six +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.pkg_resources import RequirementParseError + +from pip._internal.exceptions import InstallationError +from pip._internal.req.constructors import ( + install_req_from_editable, install_req_from_line, +) +from pip._internal.req.req_file import COMMENT_RE +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.misc import ( + dist_is_editable, get_installed_distributions, make_vcs_requirement_url, +) + +logger = logging.getLogger(__name__) + + +def freeze( + requirement=None, + find_links=None, local_only=None, user_only=None, skip_regex=None, + isolated=False, + wheel_cache=None, + exclude_editable=False, + skip=()): + find_links = find_links or [] + skip_match = None + + if skip_regex: + skip_match = re.compile(skip_regex).search + + dependency_links = [] + + for dist in pkg_resources.working_set: + if dist.has_metadata('dependency_links.txt'): + dependency_links.extend( + dist.get_metadata_lines('dependency_links.txt') + ) + for link in find_links: + if '#egg=' in link: + dependency_links.append(link) + for link in find_links: + yield '-f %s' % link + installations = {} + for dist in get_installed_distributions(local_only=local_only, + skip=(), + user_only=user_only): + try: + req = FrozenRequirement.from_dist( + dist, + dependency_links + ) + except RequirementParseError: + logger.warning( + "Could not parse requirement: %s", + dist.project_name + ) + continue + if exclude_editable and req.editable: + continue + installations[req.name] = req + + if requirement: + # the options that don't get turned into an InstallRequirement + # should only be emitted once, even if the same option is in multiple + # requirements files, so we need to keep track of what has been emitted + # so that we don't emit it again if it's seen again + emitted_options = set() + # keep track of which files a requirement is in so that we can + # give an accurate warning if a requirement appears multiple times. + req_files = collections.defaultdict(list) + for req_file_path in requirement: + with open(req_file_path) as req_file: + for line in req_file: + if (not line.strip() or + line.strip().startswith('#') or + (skip_match and skip_match(line)) or + line.startswith(( + '-r', '--requirement', + '-Z', '--always-unzip', + '-f', '--find-links', + '-i', '--index-url', + '--pre', + '--trusted-host', + '--process-dependency-links', + '--extra-index-url'))): + line = line.rstrip() + if line not in emitted_options: + emitted_options.add(line) + yield line + continue + + if line.startswith('-e') or line.startswith('--editable'): + if line.startswith('-e'): + line = line[2:].strip() + else: + line = line[len('--editable'):].strip().lstrip('=') + line_req = install_req_from_editable( + line, + isolated=isolated, + wheel_cache=wheel_cache, + ) + else: + line_req = install_req_from_line( + COMMENT_RE.sub('', line).strip(), + isolated=isolated, + wheel_cache=wheel_cache, + ) + + if not line_req.name: + logger.info( + "Skipping line in requirement file [%s] because " + "it's not clear what it would install: %s", + req_file_path, line.strip(), + ) + logger.info( + " (add #egg=PackageName to the URL to avoid" + " this warning)" + ) + elif line_req.name not in installations: + # either it's not installed, or it is installed + # but has been processed already + if not req_files[line_req.name]: + logger.warning( + "Requirement file [%s] contains %s, but that " + "package is not installed", + req_file_path, + COMMENT_RE.sub('', line).strip(), + ) + else: + req_files[line_req.name].append(req_file_path) + else: + yield str(installations[line_req.name]).rstrip() + del installations[line_req.name] + req_files[line_req.name].append(req_file_path) + + # Warn about requirements that were included multiple times (in a + # single requirements file or in different requirements files). + for name, files in six.iteritems(req_files): + if len(files) > 1: + logger.warning("Requirement %s included multiple times [%s]", + name, ', '.join(sorted(set(files)))) + + yield( + '## The following requirements were added by ' + 'pip freeze:' + ) + for installation in sorted( + installations.values(), key=lambda x: x.name.lower()): + if canonicalize_name(installation.name) not in skip: + yield str(installation).rstrip() + + +class FrozenRequirement(object): + def __init__(self, name, req, editable, comments=()): + self.name = name + self.req = req + self.editable = editable + self.comments = comments + + _rev_re = re.compile(r'-r(\d+)$') + _date_re = re.compile(r'-(20\d\d\d\d\d\d)$') + + @classmethod + def _init_args_from_dist(cls, dist, dependency_links): + """ + Compute and return arguments (req, editable, comments) to pass to + FrozenRequirement.__init__(). + + This method is for use in FrozenRequirement.from_dist(). + """ + location = os.path.normcase(os.path.abspath(dist.location)) + comments = [] + from pip._internal.vcs import vcs, get_src_requirement + if dist_is_editable(dist) and vcs.get_backend_name(location): + editable = True + try: + req = get_src_requirement(dist, location) + except InstallationError as exc: + logger.warning( + "Error when trying to get requirement for VCS system %s, " + "falling back to uneditable format", exc + ) + req = None + if req is None: + logger.warning( + 'Could not determine repository location of %s', location + ) + comments.append( + '## !! Could not determine repository location' + ) + req = dist.as_requirement() + editable = False + else: + editable = False + req = dist.as_requirement() + specs = req.specs + assert len(specs) == 1 and specs[0][0] in ["==", "==="], \ + 'Expected 1 spec with == or ===; specs = %r; dist = %r' % \ + (specs, dist) + version = specs[0][1] + ver_match = cls._rev_re.search(version) + date_match = cls._date_re.search(version) + if ver_match or date_match: + svn_backend = vcs.get_backend('svn') + if svn_backend: + svn_location = svn_backend().get_location( + dist, + dependency_links, + ) + if not svn_location: + logger.warning( + 'Warning: cannot find svn location for %s', req, + ) + comments.append( + '## FIXME: could not find svn URL in dependency_links ' + 'for this package:' + ) + else: + deprecated( + "SVN editable detection based on dependency links " + "will be dropped in the future.", + replacement=None, + gone_in="18.2", + issue=4187, + ) + comments.append( + '# Installing as editable to satisfy requirement %s:' % + req + ) + if ver_match: + rev = ver_match.group(1) + else: + rev = '{%s}' % date_match.group(1) + editable = True + egg_name = cls.egg_name(dist) + req = make_vcs_requirement_url(svn_location, rev, egg_name) + + return (req, editable, comments) + + @classmethod + def from_dist(cls, dist, dependency_links): + args = cls._init_args_from_dist(dist, dependency_links) + return cls(dist.project_name, *args) + + @staticmethod + def egg_name(dist): + name = dist.egg_name() + match = re.search(r'-py\d\.\d$', name) + if match: + name = name[:match.start()] + return name + + def __str__(self): + req = self.req + if self.editable: + req = '-e %s' % req + return '\n'.join(list(self.comments) + [str(req)]) + '\n' diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/operations/prepare.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/operations/prepare.py new file mode 100644 index 0000000..104bea3 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/operations/prepare.py @@ -0,0 +1,355 @@ +"""Prepares a distribution for installation +""" + +import logging +import os + +from pip._vendor import pkg_resources, requests + +from pip._internal.build_env import BuildEnvironment +from pip._internal.download import ( + is_dir_url, is_file_url, is_vcs_url, unpack_url, url_to_path, +) +from pip._internal.exceptions import ( + DirectoryUrlHashUnsupported, HashUnpinned, InstallationError, + PreviousBuildDirError, VcsHashUnsupported, +) +from pip._internal.utils.compat import expanduser +from pip._internal.utils.hashes import MissingHashes +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import display_path, normalize_path +from pip._internal.vcs import vcs + +logger = logging.getLogger(__name__) + + +def make_abstract_dist(req): + """Factory to make an abstract dist object. + + Preconditions: Either an editable req with a source_dir, or satisfied_by or + a wheel link, or a non-editable req with a source_dir. + + :return: A concrete DistAbstraction. + """ + if req.editable: + return IsSDist(req) + elif req.link and req.link.is_wheel: + return IsWheel(req) + else: + return IsSDist(req) + + +class DistAbstraction(object): + """Abstracts out the wheel vs non-wheel Resolver.resolve() logic. + + The requirements for anything installable are as follows: + - we must be able to determine the requirement name + (or we can't correctly handle the non-upgrade case). + - we must be able to generate a list of run-time dependencies + without installing any additional packages (or we would + have to either burn time by doing temporary isolated installs + or alternatively violate pips 'don't start installing unless + all requirements are available' rule - neither of which are + desirable). + - for packages with setup requirements, we must also be able + to determine their requirements without installing additional + packages (for the same reason as run-time dependencies) + - we must be able to create a Distribution object exposing the + above metadata. + """ + + def __init__(self, req): + self.req = req + + def dist(self, finder): + """Return a setuptools Dist object.""" + raise NotImplementedError(self.dist) + + def prep_for_dist(self, finder, build_isolation): + """Ensure that we can get a Dist for this requirement.""" + raise NotImplementedError(self.dist) + + +class IsWheel(DistAbstraction): + + def dist(self, finder): + return list(pkg_resources.find_distributions( + self.req.source_dir))[0] + + def prep_for_dist(self, finder, build_isolation): + # FIXME:https://github.com/pypa/pip/issues/1112 + pass + + +class IsSDist(DistAbstraction): + + def dist(self, finder): + dist = self.req.get_dist() + # FIXME: shouldn't be globally added. + if finder and dist.has_metadata('dependency_links.txt'): + finder.add_dependency_links( + dist.get_metadata_lines('dependency_links.txt') + ) + return dist + + def prep_for_dist(self, finder, build_isolation): + # Prepare for building. We need to: + # 1. Load pyproject.toml (if it exists) + # 2. Set up the build environment + + self.req.load_pyproject_toml() + should_isolate = self.req.use_pep517 and build_isolation + + if should_isolate: + # Isolate in a BuildEnvironment and install the build-time + # requirements. + self.req.build_env = BuildEnvironment() + self.req.build_env.install_requirements( + finder, self.req.pyproject_requires, + "Installing build dependencies" + ) + missing = [] + if self.req.requirements_to_check: + check = self.req.requirements_to_check + missing = self.req.build_env.missing_requirements(check) + if missing: + logger.warning( + "Missing build requirements in pyproject.toml for %s.", + self.req, + ) + logger.warning( + "The project does not specify a build backend, and pip " + "cannot fall back to setuptools without %s.", + " and ".join(map(repr, sorted(missing))) + ) + + self.req.run_egg_info() + self.req.assert_source_matches_version() + + +class Installed(DistAbstraction): + + def dist(self, finder): + return self.req.satisfied_by + + def prep_for_dist(self, finder, build_isolation): + pass + + +class RequirementPreparer(object): + """Prepares a Requirement + """ + + def __init__(self, build_dir, download_dir, src_dir, wheel_download_dir, + progress_bar, build_isolation, req_tracker): + super(RequirementPreparer, self).__init__() + + self.src_dir = src_dir + self.build_dir = build_dir + self.req_tracker = req_tracker + + # Where still packed archives should be written to. If None, they are + # not saved, and are deleted immediately after unpacking. + self.download_dir = download_dir + + # Where still-packed .whl files should be written to. If None, they are + # written to the download_dir parameter. Separate to download_dir to + # permit only keeping wheel archives for pip wheel. + if wheel_download_dir: + wheel_download_dir = normalize_path(wheel_download_dir) + self.wheel_download_dir = wheel_download_dir + + # NOTE + # download_dir and wheel_download_dir overlap semantically and may + # be combined if we're willing to have non-wheel archives present in + # the wheelhouse output by 'pip wheel'. + + self.progress_bar = progress_bar + + # Is build isolation allowed? + self.build_isolation = build_isolation + + @property + def _download_should_save(self): + # TODO: Modify to reduce indentation needed + if self.download_dir: + self.download_dir = expanduser(self.download_dir) + if os.path.exists(self.download_dir): + return True + else: + logger.critical('Could not find download directory') + raise InstallationError( + "Could not find or access download directory '%s'" + % display_path(self.download_dir)) + return False + + def prepare_linked_requirement(self, req, session, finder, + upgrade_allowed, require_hashes): + """Prepare a requirement that would be obtained from req.link + """ + # TODO: Breakup into smaller functions + if req.link and req.link.scheme == 'file': + path = url_to_path(req.link.url) + logger.info('Processing %s', display_path(path)) + else: + logger.info('Collecting %s', req) + + with indent_log(): + # @@ if filesystem packages are not marked + # editable in a req, a non deterministic error + # occurs when the script attempts to unpack the + # build directory + req.ensure_has_source_dir(self.build_dir) + # If a checkout exists, it's unwise to keep going. version + # inconsistencies are logged later, but do not fail the + # installation. + # FIXME: this won't upgrade when there's an existing + # package unpacked in `req.source_dir` + # package unpacked in `req.source_dir` + if os.path.exists(os.path.join(req.source_dir, 'setup.py')): + raise PreviousBuildDirError( + "pip can't proceed with requirements '%s' due to a" + " pre-existing build directory (%s). This is " + "likely due to a previous installation that failed" + ". pip is being responsible and not assuming it " + "can delete this. Please delete it and try again." + % (req, req.source_dir) + ) + req.populate_link(finder, upgrade_allowed, require_hashes) + + # We can't hit this spot and have populate_link return None. + # req.satisfied_by is None here (because we're + # guarded) and upgrade has no impact except when satisfied_by + # is not None. + # Then inside find_requirement existing_applicable -> False + # If no new versions are found, DistributionNotFound is raised, + # otherwise a result is guaranteed. + assert req.link + link = req.link + + # Now that we have the real link, we can tell what kind of + # requirements we have and raise some more informative errors + # than otherwise. (For example, we can raise VcsHashUnsupported + # for a VCS URL rather than HashMissing.) + if require_hashes: + # We could check these first 2 conditions inside + # unpack_url and save repetition of conditions, but then + # we would report less-useful error messages for + # unhashable requirements, complaining that there's no + # hash provided. + if is_vcs_url(link): + raise VcsHashUnsupported() + elif is_file_url(link) and is_dir_url(link): + raise DirectoryUrlHashUnsupported() + if not req.original_link and not req.is_pinned: + # Unpinned packages are asking for trouble when a new + # version is uploaded. This isn't a security check, but + # it saves users a surprising hash mismatch in the + # future. + # + # file:/// URLs aren't pinnable, so don't complain + # about them not being pinned. + raise HashUnpinned() + + hashes = req.hashes(trust_internet=not require_hashes) + if require_hashes and not hashes: + # Known-good hashes are missing for this requirement, so + # shim it with a facade object that will provoke hash + # computation and then raise a HashMissing exception + # showing the user what the hash should be. + hashes = MissingHashes() + + try: + download_dir = self.download_dir + # We always delete unpacked sdists after pip ran. + autodelete_unpacked = True + if req.link.is_wheel and self.wheel_download_dir: + # when doing 'pip wheel` we download wheels to a + # dedicated dir. + download_dir = self.wheel_download_dir + if req.link.is_wheel: + if download_dir: + # When downloading, we only unpack wheels to get + # metadata. + autodelete_unpacked = True + else: + # When installing a wheel, we use the unpacked + # wheel. + autodelete_unpacked = False + unpack_url( + req.link, req.source_dir, + download_dir, autodelete_unpacked, + session=session, hashes=hashes, + progress_bar=self.progress_bar + ) + except requests.HTTPError as exc: + logger.critical( + 'Could not install requirement %s because of error %s', + req, + exc, + ) + raise InstallationError( + 'Could not install requirement %s because of HTTP ' + 'error %s for URL %s' % + (req, exc, req.link) + ) + abstract_dist = make_abstract_dist(req) + with self.req_tracker.track(req): + abstract_dist.prep_for_dist(finder, self.build_isolation) + if self._download_should_save: + # Make a .zip of the source_dir we already created. + if req.link.scheme in vcs.all_schemes: + req.archive(self.download_dir) + return abstract_dist + + def prepare_editable_requirement(self, req, require_hashes, use_user_site, + finder): + """Prepare an editable requirement + """ + assert req.editable, "cannot prepare a non-editable req as editable" + + logger.info('Obtaining %s', req) + + with indent_log(): + if require_hashes: + raise InstallationError( + 'The editable requirement %s cannot be installed when ' + 'requiring hashes, because there is no single file to ' + 'hash.' % req + ) + req.ensure_has_source_dir(self.src_dir) + req.update_editable(not self._download_should_save) + + abstract_dist = make_abstract_dist(req) + with self.req_tracker.track(req): + abstract_dist.prep_for_dist(finder, self.build_isolation) + + if self._download_should_save: + req.archive(self.download_dir) + req.check_if_exists(use_user_site) + + return abstract_dist + + def prepare_installed_requirement(self, req, require_hashes, skip_reason): + """Prepare an already-installed requirement + """ + assert req.satisfied_by, "req should have been satisfied but isn't" + assert skip_reason is not None, ( + "did not get skip reason skipped but req.satisfied_by " + "is set to %r" % (req.satisfied_by,) + ) + logger.info( + 'Requirement %s: %s (%s)', + skip_reason, req, req.satisfied_by.version + ) + with indent_log(): + if require_hashes: + logger.debug( + 'Since it is already installed, we are trusting this ' + 'package without checking its hash. To ensure a ' + 'completely repeatable environment, install into an ' + 'empty virtualenv.' + ) + abstract_dist = Installed(req) + + return abstract_dist diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/pep425tags.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/pep425tags.py new file mode 100644 index 0000000..ab1a029 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/pep425tags.py @@ -0,0 +1,317 @@ +"""Generate and work with PEP 425 Compatibility Tags.""" +from __future__ import absolute_import + +import distutils.util +import logging +import platform +import re +import sys +import sysconfig +import warnings +from collections import OrderedDict + +import pip._internal.utils.glibc +from pip._internal.utils.compat import get_extension_suffixes + +logger = logging.getLogger(__name__) + +_osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)') + + +def get_config_var(var): + try: + return sysconfig.get_config_var(var) + except IOError as e: # Issue #1074 + warnings.warn("{}".format(e), RuntimeWarning) + return None + + +def get_abbr_impl(): + """Return abbreviated implementation name.""" + if hasattr(sys, 'pypy_version_info'): + pyimpl = 'pp' + elif sys.platform.startswith('java'): + pyimpl = 'jy' + elif sys.platform == 'cli': + pyimpl = 'ip' + else: + pyimpl = 'cp' + return pyimpl + + +def get_impl_ver(): + """Return implementation version.""" + impl_ver = get_config_var("py_version_nodot") + if not impl_ver or get_abbr_impl() == 'pp': + impl_ver = ''.join(map(str, get_impl_version_info())) + return impl_ver + + +def get_impl_version_info(): + """Return sys.version_info-like tuple for use in decrementing the minor + version.""" + if get_abbr_impl() == 'pp': + # as per https://github.com/pypa/pip/issues/2882 + return (sys.version_info[0], sys.pypy_version_info.major, + sys.pypy_version_info.minor) + else: + return sys.version_info[0], sys.version_info[1] + + +def get_impl_tag(): + """ + Returns the Tag for this specific implementation. + """ + return "{}{}".format(get_abbr_impl(), get_impl_ver()) + + +def get_flag(var, fallback, expected=True, warn=True): + """Use a fallback method for determining SOABI flags if the needed config + var is unset or unavailable.""" + val = get_config_var(var) + if val is None: + if warn: + logger.debug("Config variable '%s' is unset, Python ABI tag may " + "be incorrect", var) + return fallback() + return val == expected + + +def get_abi_tag(): + """Return the ABI tag based on SOABI (if available) or emulate SOABI + (CPython 2, PyPy).""" + soabi = get_config_var('SOABI') + impl = get_abbr_impl() + if not soabi and impl in {'cp', 'pp'} and hasattr(sys, 'maxunicode'): + d = '' + m = '' + u = '' + if get_flag('Py_DEBUG', + lambda: hasattr(sys, 'gettotalrefcount'), + warn=(impl == 'cp')): + d = 'd' + if get_flag('WITH_PYMALLOC', + lambda: impl == 'cp', + warn=(impl == 'cp')): + m = 'm' + if get_flag('Py_UNICODE_SIZE', + lambda: sys.maxunicode == 0x10ffff, + expected=4, + warn=(impl == 'cp' and + sys.version_info < (3, 3))) \ + and sys.version_info < (3, 3): + u = 'u' + abi = '%s%s%s%s%s' % (impl, get_impl_ver(), d, m, u) + elif soabi and soabi.startswith('cpython-'): + abi = 'cp' + soabi.split('-')[1] + elif soabi: + abi = soabi.replace('.', '_').replace('-', '_') + else: + abi = None + return abi + + +def _is_running_32bit(): + return sys.maxsize == 2147483647 + + +def get_platform(): + """Return our platform name 'win32', 'linux_x86_64'""" + if sys.platform == 'darwin': + # distutils.util.get_platform() returns the release based on the value + # of MACOSX_DEPLOYMENT_TARGET on which Python was built, which may + # be significantly older than the user's current machine. + release, _, machine = platform.mac_ver() + split_ver = release.split('.') + + if machine == "x86_64" and _is_running_32bit(): + machine = "i386" + elif machine == "ppc64" and _is_running_32bit(): + machine = "ppc" + + return 'macosx_{}_{}_{}'.format(split_ver[0], split_ver[1], machine) + + # XXX remove distutils dependency + result = distutils.util.get_platform().replace('.', '_').replace('-', '_') + if result == "linux_x86_64" and _is_running_32bit(): + # 32 bit Python program (running on a 64 bit Linux): pip should only + # install and run 32 bit compiled extensions in that case. + result = "linux_i686" + + return result + + +def is_manylinux1_compatible(): + # Only Linux, and only x86-64 / i686 + if get_platform() not in {"linux_x86_64", "linux_i686"}: + return False + + # Check for presence of _manylinux module + try: + import _manylinux + return bool(_manylinux.manylinux1_compatible) + except (ImportError, AttributeError): + # Fall through to heuristic check below + pass + + # Check glibc version. CentOS 5 uses glibc 2.5. + return pip._internal.utils.glibc.have_compatible_glibc(2, 5) + + +def get_darwin_arches(major, minor, machine): + """Return a list of supported arches (including group arches) for + the given major, minor and machine architecture of an macOS machine. + """ + arches = [] + + def _supports_arch(major, minor, arch): + # Looking at the application support for macOS versions in the chart + # provided by https://en.wikipedia.org/wiki/OS_X#Versions it appears + # our timeline looks roughly like: + # + # 10.0 - Introduces ppc support. + # 10.4 - Introduces ppc64, i386, and x86_64 support, however the ppc64 + # and x86_64 support is CLI only, and cannot be used for GUI + # applications. + # 10.5 - Extends ppc64 and x86_64 support to cover GUI applications. + # 10.6 - Drops support for ppc64 + # 10.7 - Drops support for ppc + # + # Given that we do not know if we're installing a CLI or a GUI + # application, we must be conservative and assume it might be a GUI + # application and behave as if ppc64 and x86_64 support did not occur + # until 10.5. + # + # Note: The above information is taken from the "Application support" + # column in the chart not the "Processor support" since I believe + # that we care about what instruction sets an application can use + # not which processors the OS supports. + if arch == 'ppc': + return (major, minor) <= (10, 5) + if arch == 'ppc64': + return (major, minor) == (10, 5) + if arch == 'i386': + return (major, minor) >= (10, 4) + if arch == 'x86_64': + return (major, minor) >= (10, 5) + if arch in groups: + for garch in groups[arch]: + if _supports_arch(major, minor, garch): + return True + return False + + groups = OrderedDict([ + ("fat", ("i386", "ppc")), + ("intel", ("x86_64", "i386")), + ("fat64", ("x86_64", "ppc64")), + ("fat32", ("x86_64", "i386", "ppc")), + ]) + + if _supports_arch(major, minor, machine): + arches.append(machine) + + for garch in groups: + if machine in groups[garch] and _supports_arch(major, minor, garch): + arches.append(garch) + + arches.append('universal') + + return arches + + +def get_supported(versions=None, noarch=False, platform=None, + impl=None, abi=None): + """Return a list of supported tags for each version specified in + `versions`. + + :param versions: a list of string versions, of the form ["33", "32"], + or None. The first version will be assumed to support our ABI. + :param platform: specify the exact platform you want valid + tags for, or None. If None, use the local system platform. + :param impl: specify the exact implementation you want valid + tags for, or None. If None, use the local interpreter impl. + :param abi: specify the exact abi you want valid + tags for, or None. If None, use the local interpreter abi. + """ + supported = [] + + # Versions must be given with respect to the preference + if versions is None: + versions = [] + version_info = get_impl_version_info() + major = version_info[:-1] + # Support all previous minor Python versions. + for minor in range(version_info[-1], -1, -1): + versions.append(''.join(map(str, major + (minor,)))) + + impl = impl or get_abbr_impl() + + abis = [] + + abi = abi or get_abi_tag() + if abi: + abis[0:0] = [abi] + + abi3s = set() + for suffix in get_extension_suffixes(): + if suffix.startswith('.abi'): + abi3s.add(suffix.split('.', 2)[1]) + + abis.extend(sorted(list(abi3s))) + + abis.append('none') + + if not noarch: + arch = platform or get_platform() + if arch.startswith('macosx'): + # support macosx-10.6-intel on macosx-10.9-x86_64 + match = _osx_arch_pat.match(arch) + if match: + name, major, minor, actual_arch = match.groups() + tpl = '{}_{}_%i_%s'.format(name, major) + arches = [] + for m in reversed(range(int(minor) + 1)): + for a in get_darwin_arches(int(major), m, actual_arch): + arches.append(tpl % (m, a)) + else: + # arch pattern didn't match (?!) + arches = [arch] + elif platform is None and is_manylinux1_compatible(): + arches = [arch.replace('linux', 'manylinux1'), arch] + else: + arches = [arch] + + # Current version, current API (built specifically for our Python): + for abi in abis: + for arch in arches: + supported.append(('%s%s' % (impl, versions[0]), abi, arch)) + + # abi3 modules compatible with older version of Python + for version in versions[1:]: + # abi3 was introduced in Python 3.2 + if version in {'31', '30'}: + break + for abi in abi3s: # empty set if not Python 3 + for arch in arches: + supported.append(("%s%s" % (impl, version), abi, arch)) + + # Has binaries, does not use the Python API: + for arch in arches: + supported.append(('py%s' % (versions[0][0]), 'none', arch)) + + # No abi / arch, but requires our implementation: + supported.append(('%s%s' % (impl, versions[0]), 'none', 'any')) + # Tagged specifically as being cross-version compatible + # (with just the major version specified) + supported.append(('%s%s' % (impl, versions[0][0]), 'none', 'any')) + + # No abi / arch, generic Python + for i, version in enumerate(versions): + supported.append(('py%s' % (version,), 'none', 'any')) + if i == 0: + supported.append(('py%s' % (version[0]), 'none', 'any')) + + return supported + + +implementation_tag = get_impl_tag() diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/pyproject.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/pyproject.py new file mode 100644 index 0000000..f938a76 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/pyproject.py @@ -0,0 +1,144 @@ +from __future__ import absolute_import + +import io +import os + +from pip._vendor import pytoml, six + +from pip._internal.exceptions import InstallationError + + +def _is_list_of_str(obj): + return ( + isinstance(obj, list) and + all(isinstance(item, six.string_types) for item in obj) + ) + + +def load_pyproject_toml(use_pep517, pyproject_toml, setup_py, req_name): + """Load the pyproject.toml file. + + Parameters: + use_pep517 - Has the user requested PEP 517 processing? None + means the user hasn't explicitly specified. + pyproject_toml - Location of the project's pyproject.toml file + setup_py - Location of the project's setup.py file + req_name - The name of the requirement we're processing (for + error reporting) + + Returns: + None if we should use the legacy code path, otherwise a tuple + ( + requirements from pyproject.toml, + name of PEP 517 backend, + requirements we should check are installed after setting + up the build environment + ) + """ + has_pyproject = os.path.isfile(pyproject_toml) + has_setup = os.path.isfile(setup_py) + + if has_pyproject: + with io.open(pyproject_toml, encoding="utf-8") as f: + pp_toml = pytoml.load(f) + build_system = pp_toml.get("build-system") + else: + build_system = None + + # The following cases must use PEP 517 + # We check for use_pep517 equalling False because that + # means the user explicitly requested --no-use-pep517 + if has_pyproject and not has_setup: + if use_pep517 is False: + raise InstallationError( + "Disabling PEP 517 processing is invalid: " + "project does not have a setup.py" + ) + use_pep517 = True + elif build_system and "build-backend" in build_system: + if use_pep517 is False: + raise InstallationError( + "Disabling PEP 517 processing is invalid: " + "project specifies a build backend of {} " + "in pyproject.toml".format( + build_system["build-backend"] + ) + ) + use_pep517 = True + + # If we haven't worked out whether to use PEP 517 yet, + # and the user hasn't explicitly stated a preference, + # we do so if the project has a pyproject.toml file. + elif use_pep517 is None: + use_pep517 = has_pyproject + + # At this point, we know whether we're going to use PEP 517. + assert use_pep517 is not None + + # If we're using the legacy code path, there is nothing further + # for us to do here. + if not use_pep517: + return None + + if build_system is None: + # Either the user has a pyproject.toml with no build-system + # section, or the user has no pyproject.toml, but has opted in + # explicitly via --use-pep517. + # In the absence of any explicit backend specification, we + # assume the setuptools backend, and require wheel and a version + # of setuptools that supports that backend. + build_system = { + "requires": ["setuptools>=38.2.5", "wheel"], + "build-backend": "setuptools.build_meta", + } + + # If we're using PEP 517, we have build system information (either + # from pyproject.toml, or defaulted by the code above). + # Note that at this point, we do not know if the user has actually + # specified a backend, though. + assert build_system is not None + + # Ensure that the build-system section in pyproject.toml conforms + # to PEP 518. + error_template = ( + "{package} has a pyproject.toml file that does not comply " + "with PEP 518: {reason}" + ) + + # Specifying the build-system table but not the requires key is invalid + if "requires" not in build_system: + raise InstallationError( + error_template.format(package=req_name, reason=( + "it has a 'build-system' table but not " + "'build-system.requires' which is mandatory in the table" + )) + ) + + # Error out if requires is not a list of strings + requires = build_system["requires"] + if not _is_list_of_str(requires): + raise InstallationError(error_template.format( + package=req_name, + reason="'build-system.requires' is not a list of strings.", + )) + + backend = build_system.get("build-backend") + check = [] + if backend is None: + # If the user didn't specify a backend, we assume they want to use + # the setuptools backend. But we can't be sure they have included + # a version of setuptools which supplies the backend, or wheel + # (which is neede by the backend) in their requirements. So we + # make a note to check that those requirements are present once + # we have set up the environment. + # TODO: Review this - it's quite a lot of work to check for a very + # specific case. The problem is, that case is potentially quite + # common - projects that adopted PEP 518 early for the ability to + # specify requirements to execute setup.py, but never considered + # needing to mention the build tools themselves. The original PEP + # 518 code had a similar check (but implemented in a different + # way). + backend = "setuptools.build_meta" + check = ["setuptools>=38.2.5", "wheel"] + + return (requires, backend, check) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/__init__.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/__init__.py new file mode 100644 index 0000000..b270498 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/__init__.py @@ -0,0 +1,69 @@ +from __future__ import absolute_import + +import logging + +from .req_install import InstallRequirement +from .req_set import RequirementSet +from .req_file import parse_requirements +from pip._internal.utils.logging import indent_log + + +__all__ = [ + "RequirementSet", "InstallRequirement", + "parse_requirements", "install_given_reqs", +] + +logger = logging.getLogger(__name__) + + +def install_given_reqs(to_install, install_options, global_options=(), + *args, **kwargs): + """ + Install everything in the given list. + + (to be called after having downloaded and unpacked the packages) + """ + + if to_install: + logger.info( + 'Installing collected packages: %s', + ', '.join([req.name for req in to_install]), + ) + + with indent_log(): + for requirement in to_install: + if requirement.conflicts_with: + logger.info( + 'Found existing installation: %s', + requirement.conflicts_with, + ) + with indent_log(): + uninstalled_pathset = requirement.uninstall( + auto_confirm=True + ) + try: + requirement.install( + install_options, + global_options, + *args, + **kwargs + ) + except Exception: + should_rollback = ( + requirement.conflicts_with and + not requirement.install_succeeded + ) + # if install did not succeed, rollback previous uninstall + if should_rollback: + uninstalled_pathset.rollback() + raise + else: + should_commit = ( + requirement.conflicts_with and + requirement.install_succeeded + ) + if should_commit: + uninstalled_pathset.commit() + requirement.remove_temporary_source() + + return to_install diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/constructors.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/constructors.py new file mode 100644 index 0000000..4c4641d --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/constructors.py @@ -0,0 +1,298 @@ +"""Backing implementation for InstallRequirement's various constructors + +The idea here is that these formed a major chunk of InstallRequirement's size +so, moving them and support code dedicated to them outside of that class +helps creates for better understandability for the rest of the code. + +These are meant to be used elsewhere within pip to create instances of +InstallRequirement. +""" + +import logging +import os +import re +import traceback + +from pip._vendor.packaging.markers import Marker +from pip._vendor.packaging.requirements import InvalidRequirement, Requirement +from pip._vendor.packaging.specifiers import Specifier +from pip._vendor.pkg_resources import RequirementParseError, parse_requirements + +from pip._internal.download import ( + is_archive_file, is_url, path_to_url, url_to_path, +) +from pip._internal.exceptions import InstallationError +from pip._internal.models.index import PyPI, TestPyPI +from pip._internal.models.link import Link +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.misc import is_installable_dir +from pip._internal.vcs import vcs +from pip._internal.wheel import Wheel + +__all__ = [ + "install_req_from_editable", "install_req_from_line", + "parse_editable" +] + +logger = logging.getLogger(__name__) +operators = Specifier._operators.keys() + + +def _strip_extras(path): + m = re.match(r'^(.+)(\[[^\]]+\])$', path) + extras = None + if m: + path_no_extras = m.group(1) + extras = m.group(2) + else: + path_no_extras = path + + return path_no_extras, extras + + +def parse_editable(editable_req): + """Parses an editable requirement into: + - a requirement name + - an URL + - extras + - editable options + Accepted requirements: + svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir + .[some_extra] + """ + + url = editable_req + + # If a file path is specified with extras, strip off the extras. + url_no_extras, extras = _strip_extras(url) + + if os.path.isdir(url_no_extras): + if not os.path.exists(os.path.join(url_no_extras, 'setup.py')): + raise InstallationError( + "Directory %r is not installable. File 'setup.py' not found." % + url_no_extras + ) + # Treating it as code that has already been checked out + url_no_extras = path_to_url(url_no_extras) + + if url_no_extras.lower().startswith('file:'): + package_name = Link(url_no_extras).egg_fragment + if extras: + return ( + package_name, + url_no_extras, + Requirement("placeholder" + extras.lower()).extras, + ) + else: + return package_name, url_no_extras, None + + for version_control in vcs: + if url.lower().startswith('%s:' % version_control): + url = '%s+%s' % (version_control, url) + break + + if '+' not in url: + raise InstallationError( + '%s should either be a path to a local project or a VCS url ' + 'beginning with svn+, git+, hg+, or bzr+' % + editable_req + ) + + vc_type = url.split('+', 1)[0].lower() + + if not vcs.get_backend(vc_type): + error_message = 'For --editable=%s only ' % editable_req + \ + ', '.join([backend.name + '+URL' for backend in vcs.backends]) + \ + ' is currently supported' + raise InstallationError(error_message) + + package_name = Link(url).egg_fragment + if not package_name: + raise InstallationError( + "Could not detect requirement name for '%s', please specify one " + "with #egg=your_package_name" % editable_req + ) + return package_name, url, None + + +def deduce_helpful_msg(req): + """Returns helpful msg in case requirements file does not exist, + or cannot be parsed. + + :params req: Requirements file path + """ + msg = "" + if os.path.exists(req): + msg = " It does exist." + # Try to parse and check if it is a requirements file. + try: + with open(req, 'r') as fp: + # parse first line only + next(parse_requirements(fp.read())) + msg += " The argument you provided " + \ + "(%s) appears to be a" % (req) + \ + " requirements file. If that is the" + \ + " case, use the '-r' flag to install" + \ + " the packages specified within it." + except RequirementParseError: + logger.debug("Cannot parse '%s' as requirements \ + file" % (req), exc_info=1) + else: + msg += " File '%s' does not exist." % (req) + return msg + + +# ---- The actual constructors follow ---- + + +def install_req_from_editable( + editable_req, comes_from=None, isolated=False, options=None, + wheel_cache=None, constraint=False +): + name, url, extras_override = parse_editable(editable_req) + if url.startswith('file:'): + source_dir = url_to_path(url) + else: + source_dir = None + + if name is not None: + try: + req = Requirement(name) + except InvalidRequirement: + raise InstallationError("Invalid requirement: '%s'" % name) + else: + req = None + return InstallRequirement( + req, comes_from, source_dir=source_dir, + editable=True, + link=Link(url), + constraint=constraint, + isolated=isolated, + options=options if options else {}, + wheel_cache=wheel_cache, + extras=extras_override or (), + ) + + +def install_req_from_line( + name, comes_from=None, isolated=False, options=None, wheel_cache=None, + constraint=False +): + """Creates an InstallRequirement from a name, which might be a + requirement, directory containing 'setup.py', filename, or URL. + """ + if is_url(name): + marker_sep = '; ' + else: + marker_sep = ';' + if marker_sep in name: + name, markers = name.split(marker_sep, 1) + markers = markers.strip() + if not markers: + markers = None + else: + markers = Marker(markers) + else: + markers = None + name = name.strip() + req = None + path = os.path.normpath(os.path.abspath(name)) + link = None + extras = None + + if is_url(name): + link = Link(name) + else: + p, extras = _strip_extras(path) + looks_like_dir = os.path.isdir(p) and ( + os.path.sep in name or + (os.path.altsep is not None and os.path.altsep in name) or + name.startswith('.') + ) + if looks_like_dir: + if not is_installable_dir(p): + raise InstallationError( + "Directory %r is not installable. Neither 'setup.py' " + "nor 'pyproject.toml' found." % name + ) + link = Link(path_to_url(p)) + elif is_archive_file(p): + if not os.path.isfile(p): + logger.warning( + 'Requirement %r looks like a filename, but the ' + 'file does not exist', + name + ) + link = Link(path_to_url(p)) + + # it's a local file, dir, or url + if link: + # Handle relative file URLs + if link.scheme == 'file' and re.search(r'\.\./', link.url): + link = Link( + path_to_url(os.path.normpath(os.path.abspath(link.path)))) + # wheel file + if link.is_wheel: + wheel = Wheel(link.filename) # can raise InvalidWheelFilename + req = "%s==%s" % (wheel.name, wheel.version) + else: + # set the req to the egg fragment. when it's not there, this + # will become an 'unnamed' requirement + req = link.egg_fragment + + # a requirement specifier + else: + req = name + + if extras: + extras = Requirement("placeholder" + extras.lower()).extras + else: + extras = () + if req is not None: + try: + req = Requirement(req) + except InvalidRequirement: + if os.path.sep in req: + add_msg = "It looks like a path." + add_msg += deduce_helpful_msg(req) + elif '=' in req and not any(op in req for op in operators): + add_msg = "= is not a valid operator. Did you mean == ?" + else: + add_msg = traceback.format_exc() + raise InstallationError( + "Invalid requirement: '%s'\n%s" % (req, add_msg) + ) + + return InstallRequirement( + req, comes_from, link=link, markers=markers, + isolated=isolated, + options=options if options else {}, + wheel_cache=wheel_cache, + constraint=constraint, + extras=extras, + ) + + +def install_req_from_req( + req, comes_from=None, isolated=False, wheel_cache=None +): + try: + req = Requirement(req) + except InvalidRequirement: + raise InstallationError("Invalid requirement: '%s'" % req) + + domains_not_allowed = [ + PyPI.file_storage_domain, + TestPyPI.file_storage_domain, + ] + if req.url and comes_from.link.netloc in domains_not_allowed: + # Explicitly disallow pypi packages that depend on external urls + raise InstallationError( + "Packages installed from PyPI cannot depend on packages " + "which are not also hosted on PyPI.\n" + "%s depends on %s " % (comes_from.name, req) + ) + + return InstallRequirement( + req, comes_from, isolated=isolated, wheel_cache=wheel_cache + ) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/req_file.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/req_file.py new file mode 100644 index 0000000..e7acf7c --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/req_file.py @@ -0,0 +1,340 @@ +""" +Requirements file parsing +""" + +from __future__ import absolute_import + +import optparse +import os +import re +import shlex +import sys + +from pip._vendor.six.moves import filterfalse +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.cli import cmdoptions +from pip._internal.download import get_file_content +from pip._internal.exceptions import RequirementsFileParseError +from pip._internal.req.constructors import ( + install_req_from_editable, install_req_from_line, +) + +__all__ = ['parse_requirements'] + +SCHEME_RE = re.compile(r'^(http|https|file):', re.I) +COMMENT_RE = re.compile(r'(^|\s)+#.*$') + +# Matches environment variable-style values in '${MY_VARIABLE_1}' with the +# variable name consisting of only uppercase letters, digits or the '_' +# (underscore). This follows the POSIX standard defined in IEEE Std 1003.1, +# 2013 Edition. +ENV_VAR_RE = re.compile(r'(?P\$\{(?P[A-Z0-9_]+)\})') + +SUPPORTED_OPTIONS = [ + cmdoptions.constraints, + cmdoptions.editable, + cmdoptions.requirements, + cmdoptions.no_index, + cmdoptions.index_url, + cmdoptions.find_links, + cmdoptions.extra_index_url, + cmdoptions.always_unzip, + cmdoptions.no_binary, + cmdoptions.only_binary, + cmdoptions.pre, + cmdoptions.process_dependency_links, + cmdoptions.trusted_host, + cmdoptions.require_hashes, +] + +# options to be passed to requirements +SUPPORTED_OPTIONS_REQ = [ + cmdoptions.install_options, + cmdoptions.global_options, + cmdoptions.hash, +] + +# the 'dest' string values +SUPPORTED_OPTIONS_REQ_DEST = [o().dest for o in SUPPORTED_OPTIONS_REQ] + + +def parse_requirements(filename, finder=None, comes_from=None, options=None, + session=None, constraint=False, wheel_cache=None): + """Parse a requirements file and yield InstallRequirement instances. + + :param filename: Path or url of requirements file. + :param finder: Instance of pip.index.PackageFinder. + :param comes_from: Origin description of requirements. + :param options: cli options. + :param session: Instance of pip.download.PipSession. + :param constraint: If true, parsing a constraint file rather than + requirements file. + :param wheel_cache: Instance of pip.wheel.WheelCache + """ + if session is None: + raise TypeError( + "parse_requirements() missing 1 required keyword argument: " + "'session'" + ) + + _, content = get_file_content( + filename, comes_from=comes_from, session=session + ) + + lines_enum = preprocess(content, options) + + for line_number, line in lines_enum: + req_iter = process_line(line, filename, line_number, finder, + comes_from, options, session, wheel_cache, + constraint=constraint) + for req in req_iter: + yield req + + +def preprocess(content, options): + """Split, filter, and join lines, and return a line iterator + + :param content: the content of the requirements file + :param options: cli options + """ + lines_enum = enumerate(content.splitlines(), start=1) + lines_enum = join_lines(lines_enum) + lines_enum = ignore_comments(lines_enum) + lines_enum = skip_regex(lines_enum, options) + lines_enum = expand_env_variables(lines_enum) + return lines_enum + + +def process_line(line, filename, line_number, finder=None, comes_from=None, + options=None, session=None, wheel_cache=None, + constraint=False): + """Process a single requirements line; This can result in creating/yielding + requirements, or updating the finder. + + For lines that contain requirements, the only options that have an effect + are from SUPPORTED_OPTIONS_REQ, and they are scoped to the + requirement. Other options from SUPPORTED_OPTIONS may be present, but are + ignored. + + For lines that do not contain requirements, the only options that have an + effect are from SUPPORTED_OPTIONS. Options from SUPPORTED_OPTIONS_REQ may + be present, but are ignored. These lines may contain multiple options + (although our docs imply only one is supported), and all our parsed and + affect the finder. + + :param constraint: If True, parsing a constraints file. + :param options: OptionParser options that we may update + """ + parser = build_parser(line) + defaults = parser.get_default_values() + defaults.index_url = None + if finder: + # `finder.format_control` will be updated during parsing + defaults.format_control = finder.format_control + args_str, options_str = break_args_options(line) + if sys.version_info < (2, 7, 3): + # Prior to 2.7.3, shlex cannot deal with unicode entries + options_str = options_str.encode('utf8') + opts, _ = parser.parse_args(shlex.split(options_str), defaults) + + # preserve for the nested code path + line_comes_from = '%s %s (line %s)' % ( + '-c' if constraint else '-r', filename, line_number, + ) + + # yield a line requirement + if args_str: + isolated = options.isolated_mode if options else False + if options: + cmdoptions.check_install_build_global(options, opts) + # get the options that apply to requirements + req_options = {} + for dest in SUPPORTED_OPTIONS_REQ_DEST: + if dest in opts.__dict__ and opts.__dict__[dest]: + req_options[dest] = opts.__dict__[dest] + yield install_req_from_line( + args_str, line_comes_from, constraint=constraint, + isolated=isolated, options=req_options, wheel_cache=wheel_cache + ) + + # yield an editable requirement + elif opts.editables: + isolated = options.isolated_mode if options else False + yield install_req_from_editable( + opts.editables[0], comes_from=line_comes_from, + constraint=constraint, isolated=isolated, wheel_cache=wheel_cache + ) + + # parse a nested requirements file + elif opts.requirements or opts.constraints: + if opts.requirements: + req_path = opts.requirements[0] + nested_constraint = False + else: + req_path = opts.constraints[0] + nested_constraint = True + # original file is over http + if SCHEME_RE.search(filename): + # do a url join so relative paths work + req_path = urllib_parse.urljoin(filename, req_path) + # original file and nested file are paths + elif not SCHEME_RE.search(req_path): + # do a join so relative paths work + req_path = os.path.join(os.path.dirname(filename), req_path) + # TODO: Why not use `comes_from='-r {} (line {})'` here as well? + parser = parse_requirements( + req_path, finder, comes_from, options, session, + constraint=nested_constraint, wheel_cache=wheel_cache + ) + for req in parser: + yield req + + # percolate hash-checking option upward + elif opts.require_hashes: + options.require_hashes = opts.require_hashes + + # set finder options + elif finder: + if opts.index_url: + finder.index_urls = [opts.index_url] + if opts.no_index is True: + finder.index_urls = [] + if opts.extra_index_urls: + finder.index_urls.extend(opts.extra_index_urls) + if opts.find_links: + # FIXME: it would be nice to keep track of the source + # of the find_links: support a find-links local path + # relative to a requirements file. + value = opts.find_links[0] + req_dir = os.path.dirname(os.path.abspath(filename)) + relative_to_reqs_file = os.path.join(req_dir, value) + if os.path.exists(relative_to_reqs_file): + value = relative_to_reqs_file + finder.find_links.append(value) + if opts.pre: + finder.allow_all_prereleases = True + if opts.process_dependency_links: + finder.process_dependency_links = True + if opts.trusted_hosts: + finder.secure_origins.extend( + ("*", host, "*") for host in opts.trusted_hosts) + + +def break_args_options(line): + """Break up the line into an args and options string. We only want to shlex + (and then optparse) the options, not the args. args can contain markers + which are corrupted by shlex. + """ + tokens = line.split(' ') + args = [] + options = tokens[:] + for token in tokens: + if token.startswith('-') or token.startswith('--'): + break + else: + args.append(token) + options.pop(0) + return ' '.join(args), ' '.join(options) + + +def build_parser(line): + """ + Return a parser for parsing requirement lines + """ + parser = optparse.OptionParser(add_help_option=False) + + option_factories = SUPPORTED_OPTIONS + SUPPORTED_OPTIONS_REQ + for option_factory in option_factories: + option = option_factory() + parser.add_option(option) + + # By default optparse sys.exits on parsing errors. We want to wrap + # that in our own exception. + def parser_exit(self, msg): + # add offending line + msg = 'Invalid requirement: %s\n%s' % (line, msg) + raise RequirementsFileParseError(msg) + parser.exit = parser_exit + + return parser + + +def join_lines(lines_enum): + """Joins a line ending in '\' with the previous line (except when following + comments). The joined line takes on the index of the first line. + """ + primary_line_number = None + new_line = [] + for line_number, line in lines_enum: + if not line.endswith('\\') or COMMENT_RE.match(line): + if COMMENT_RE.match(line): + # this ensures comments are always matched later + line = ' ' + line + if new_line: + new_line.append(line) + yield primary_line_number, ''.join(new_line) + new_line = [] + else: + yield line_number, line + else: + if not new_line: + primary_line_number = line_number + new_line.append(line.strip('\\')) + + # last line contains \ + if new_line: + yield primary_line_number, ''.join(new_line) + + # TODO: handle space after '\'. + + +def ignore_comments(lines_enum): + """ + Strips comments and filter empty lines. + """ + for line_number, line in lines_enum: + line = COMMENT_RE.sub('', line) + line = line.strip() + if line: + yield line_number, line + + +def skip_regex(lines_enum, options): + """ + Skip lines that match '--skip-requirements-regex' pattern + + Note: the regex pattern is only built once + """ + skip_regex = options.skip_requirements_regex if options else None + if skip_regex: + pattern = re.compile(skip_regex) + lines_enum = filterfalse(lambda e: pattern.search(e[1]), lines_enum) + return lines_enum + + +def expand_env_variables(lines_enum): + """Replace all environment variables that can be retrieved via `os.getenv`. + + The only allowed format for environment variables defined in the + requirement file is `${MY_VARIABLE_1}` to ensure two things: + + 1. Strings that contain a `$` aren't accidentally (partially) expanded. + 2. Ensure consistency across platforms for requirement files. + + These points are the result of a discusssion on the `github pull + request #3514 `_. + + Valid characters in variable names follow the `POSIX standard + `_ and are limited + to uppercase letter, digits and the `_` (underscore). + """ + for line_number, line in lines_enum: + for env_var, var_name in ENV_VAR_RE.findall(line): + value = os.getenv(var_name) + if not value: + continue + + line = line.replace(env_var, value) + + yield line_number, line diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/req_install.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/req_install.py new file mode 100644 index 0000000..c2624fe --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/req_install.py @@ -0,0 +1,860 @@ +from __future__ import absolute_import + +import logging +import os +import shutil +import sys +import sysconfig +import zipfile +from distutils.util import change_root + +from pip._vendor import pkg_resources, six +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import Version +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.pep517.wrappers import Pep517HookCaller + +from pip._internal import wheel +from pip._internal.build_env import NoOpBuildEnvironment +from pip._internal.exceptions import InstallationError +from pip._internal.locations import ( + PIP_DELETE_MARKER_FILENAME, running_under_virtualenv, +) +from pip._internal.models.link import Link +from pip._internal.pyproject import load_pyproject_toml +from pip._internal.req.req_uninstall import UninstallPathSet +from pip._internal.utils.compat import native_str +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + _make_build_dir, ask_path_exists, backup_dir, call_subprocess, + display_path, dist_in_site_packages, dist_in_usersite, ensure_dir, + get_installed_version, rmtree, +) +from pip._internal.utils.packaging import get_metadata +from pip._internal.utils.setuptools_build import SETUPTOOLS_SHIM +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.ui import open_spinner +from pip._internal.vcs import vcs +from pip._internal.wheel import move_wheel_files + +logger = logging.getLogger(__name__) + + +class InstallRequirement(object): + """ + Represents something that may be installed later on, may have information + about where to fetch the relavant requirement and also contains logic for + installing the said requirement. + """ + + def __init__(self, req, comes_from, source_dir=None, editable=False, + link=None, update=True, markers=None, + isolated=False, options=None, wheel_cache=None, + constraint=False, extras=()): + assert req is None or isinstance(req, Requirement), req + self.req = req + self.comes_from = comes_from + self.constraint = constraint + if source_dir is not None: + self.source_dir = os.path.normpath(os.path.abspath(source_dir)) + else: + self.source_dir = None + self.editable = editable + + self._wheel_cache = wheel_cache + if link is not None: + self.link = self.original_link = link + else: + self.link = self.original_link = req and req.url and Link(req.url) + + if extras: + self.extras = extras + elif req: + self.extras = { + pkg_resources.safe_extra(extra) for extra in req.extras + } + else: + self.extras = set() + if markers is not None: + self.markers = markers + else: + self.markers = req and req.marker + self._egg_info_path = None + # This holds the pkg_resources.Distribution object if this requirement + # is already available: + self.satisfied_by = None + # This hold the pkg_resources.Distribution object if this requirement + # conflicts with another installed distribution: + self.conflicts_with = None + # Temporary build location + self._temp_build_dir = TempDirectory(kind="req-build") + # Used to store the global directory where the _temp_build_dir should + # have been created. Cf _correct_build_location method. + self._ideal_build_dir = None + # True if the editable should be updated: + self.update = update + # Set to True after successful installation + self.install_succeeded = None + # UninstallPathSet of uninstalled distribution (for possible rollback) + self.uninstalled_pathset = None + self.options = options if options else {} + # Set to True after successful preparation of this requirement + self.prepared = False + self.is_direct = False + + self.isolated = isolated + self.build_env = NoOpBuildEnvironment() + + # The static build requirements (from pyproject.toml) + self.pyproject_requires = None + + # Build requirements that we will check are available + # TODO: We don't do this for --no-build-isolation. Should we? + self.requirements_to_check = [] + + # The PEP 517 backend we should use to build the project + self.pep517_backend = None + + # Are we using PEP 517 for this requirement? + # After pyproject.toml has been loaded, the only valid values are True + # and False. Before loading, None is valid (meaning "use the default"). + # Setting an explicit value before loading pyproject.toml is supported, + # but after loading this flag should be treated as read only. + self.use_pep517 = None + + def __str__(self): + if self.req: + s = str(self.req) + if self.link: + s += ' from %s' % self.link.url + elif self.link: + s = self.link.url + else: + s = '' + if self.satisfied_by is not None: + s += ' in %s' % display_path(self.satisfied_by.location) + if self.comes_from: + if isinstance(self.comes_from, six.string_types): + comes_from = self.comes_from + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += ' (from %s)' % comes_from + return s + + def __repr__(self): + return '<%s object: %s editable=%r>' % ( + self.__class__.__name__, str(self), self.editable) + + def populate_link(self, finder, upgrade, require_hashes): + """Ensure that if a link can be found for this, that it is found. + + Note that self.link may still be None - if Upgrade is False and the + requirement is already installed. + + If require_hashes is True, don't use the wheel cache, because cached + wheels, always built locally, have different hashes than the files + downloaded from the index server and thus throw false hash mismatches. + Furthermore, cached wheels at present have undeterministic contents due + to file modification times. + """ + if self.link is None: + self.link = finder.find_requirement(self, upgrade) + if self._wheel_cache is not None and not require_hashes: + old_link = self.link + self.link = self._wheel_cache.get(self.link, self.name) + if old_link != self.link: + logger.debug('Using cached wheel link: %s', self.link) + + # Things that are valid for all kinds of requirements? + @property + def name(self): + if self.req is None: + return None + return native_str(pkg_resources.safe_name(self.req.name)) + + @property + def specifier(self): + return self.req.specifier + + @property + def is_pinned(self): + """Return whether I am pinned to an exact version. + + For example, some-package==1.2 is pinned; some-package>1.2 is not. + """ + specifiers = self.specifier + return (len(specifiers) == 1 and + next(iter(specifiers)).operator in {'==', '==='}) + + @property + def installed_version(self): + return get_installed_version(self.name) + + def match_markers(self, extras_requested=None): + if not extras_requested: + # Provide an extra to safely evaluate the markers + # without matching any extra + extras_requested = ('',) + if self.markers is not None: + return any( + self.markers.evaluate({'extra': extra}) + for extra in extras_requested) + else: + return True + + @property + def has_hash_options(self): + """Return whether any known-good hashes are specified as options. + + These activate --require-hashes mode; hashes specified as part of a + URL do not. + + """ + return bool(self.options.get('hashes', {})) + + def hashes(self, trust_internet=True): + """Return a hash-comparer that considers my option- and URL-based + hashes to be known-good. + + Hashes in URLs--ones embedded in the requirements file, not ones + downloaded from an index server--are almost peers with ones from + flags. They satisfy --require-hashes (whether it was implicitly or + explicitly activated) but do not activate it. md5 and sha224 are not + allowed in flags, which should nudge people toward good algos. We + always OR all hashes together, even ones from URLs. + + :param trust_internet: Whether to trust URL-based (#md5=...) hashes + downloaded from the internet, as by populate_link() + + """ + good_hashes = self.options.get('hashes', {}).copy() + link = self.link if trust_internet else self.original_link + if link and link.hash: + good_hashes.setdefault(link.hash_name, []).append(link.hash) + return Hashes(good_hashes) + + def from_path(self): + """Format a nice indicator to show where this "comes from" + """ + if self.req is None: + return None + s = str(self.req) + if self.comes_from: + if isinstance(self.comes_from, six.string_types): + comes_from = self.comes_from + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += '->' + comes_from + return s + + def build_location(self, build_dir): + assert build_dir is not None + if self._temp_build_dir.path is not None: + return self._temp_build_dir.path + if self.req is None: + # for requirement via a path to a directory: the name of the + # package is not available yet so we create a temp directory + # Once run_egg_info will have run, we'll be able + # to fix it via _correct_build_location + # Some systems have /tmp as a symlink which confuses custom + # builds (such as numpy). Thus, we ensure that the real path + # is returned. + self._temp_build_dir.create() + self._ideal_build_dir = build_dir + + return self._temp_build_dir.path + if self.editable: + name = self.name.lower() + else: + name = self.name + # FIXME: Is there a better place to create the build_dir? (hg and bzr + # need this) + if not os.path.exists(build_dir): + logger.debug('Creating directory %s', build_dir) + _make_build_dir(build_dir) + return os.path.join(build_dir, name) + + def _correct_build_location(self): + """Move self._temp_build_dir to self._ideal_build_dir/self.req.name + + For some requirements (e.g. a path to a directory), the name of the + package is not available until we run egg_info, so the build_location + will return a temporary directory and store the _ideal_build_dir. + + This is only called by self.run_egg_info to fix the temporary build + directory. + """ + if self.source_dir is not None: + return + assert self.req is not None + assert self._temp_build_dir.path + assert self._ideal_build_dir.path + old_location = self._temp_build_dir.path + self._temp_build_dir.path = None + + new_location = self.build_location(self._ideal_build_dir) + if os.path.exists(new_location): + raise InstallationError( + 'A package already exists in %s; please remove it to continue' + % display_path(new_location)) + logger.debug( + 'Moving package %s from %s to new location %s', + self, display_path(old_location), display_path(new_location), + ) + shutil.move(old_location, new_location) + self._temp_build_dir.path = new_location + self._ideal_build_dir = None + self.source_dir = os.path.normpath(os.path.abspath(new_location)) + self._egg_info_path = None + + def remove_temporary_source(self): + """Remove the source files from this requirement, if they are marked + for deletion""" + if self.source_dir and os.path.exists( + os.path.join(self.source_dir, PIP_DELETE_MARKER_FILENAME)): + logger.debug('Removing source in %s', self.source_dir) + rmtree(self.source_dir) + self.source_dir = None + self._temp_build_dir.cleanup() + self.build_env.cleanup() + + def check_if_exists(self, use_user_site): + """Find an installed distribution that satisfies or conflicts + with this requirement, and set self.satisfied_by or + self.conflicts_with appropriately. + """ + if self.req is None: + return False + try: + # get_distribution() will resolve the entire list of requirements + # anyway, and we've already determined that we need the requirement + # in question, so strip the marker so that we don't try to + # evaluate it. + no_marker = Requirement(str(self.req)) + no_marker.marker = None + self.satisfied_by = pkg_resources.get_distribution(str(no_marker)) + if self.editable and self.satisfied_by: + self.conflicts_with = self.satisfied_by + # when installing editables, nothing pre-existing should ever + # satisfy + self.satisfied_by = None + return True + except pkg_resources.DistributionNotFound: + return False + except pkg_resources.VersionConflict: + existing_dist = pkg_resources.get_distribution( + self.req.name + ) + if use_user_site: + if dist_in_usersite(existing_dist): + self.conflicts_with = existing_dist + elif (running_under_virtualenv() and + dist_in_site_packages(existing_dist)): + raise InstallationError( + "Will not install to the user site because it will " + "lack sys.path precedence to %s in %s" % + (existing_dist.project_name, existing_dist.location) + ) + else: + self.conflicts_with = existing_dist + return True + + # Things valid for wheels + @property + def is_wheel(self): + return self.link and self.link.is_wheel + + def move_wheel_files(self, wheeldir, root=None, home=None, prefix=None, + warn_script_location=True, use_user_site=False, + pycompile=True): + move_wheel_files( + self.name, self.req, wheeldir, + user=use_user_site, + home=home, + root=root, + prefix=prefix, + pycompile=pycompile, + isolated=self.isolated, + warn_script_location=warn_script_location, + ) + + # Things valid for sdists + @property + def setup_py_dir(self): + return os.path.join( + self.source_dir, + self.link and self.link.subdirectory_fragment or '') + + @property + def setup_py(self): + assert self.source_dir, "No source dir for %s" % self + + setup_py = os.path.join(self.setup_py_dir, 'setup.py') + + # Python2 __file__ should not be unicode + if six.PY2 and isinstance(setup_py, six.text_type): + setup_py = setup_py.encode(sys.getfilesystemencoding()) + + return setup_py + + @property + def pyproject_toml(self): + assert self.source_dir, "No source dir for %s" % self + + pp_toml = os.path.join(self.setup_py_dir, 'pyproject.toml') + + # Python2 __file__ should not be unicode + if six.PY2 and isinstance(pp_toml, six.text_type): + pp_toml = pp_toml.encode(sys.getfilesystemencoding()) + + return pp_toml + + def load_pyproject_toml(self): + """Load the pyproject.toml file. + + After calling this routine, all of the attributes related to PEP 517 + processing for this requirement have been set. In particular, the + use_pep517 attribute can be used to determine whether we should + follow the PEP 517 or legacy (setup.py) code path. + """ + pep517_data = load_pyproject_toml( + self.use_pep517, + self.pyproject_toml, + self.setup_py, + str(self) + ) + + if pep517_data is None: + self.use_pep517 = False + else: + self.use_pep517 = True + requires, backend, check = pep517_data + self.requirements_to_check = check + self.pyproject_requires = requires + self.pep517_backend = Pep517HookCaller(self.setup_py_dir, backend) + + def run_egg_info(self): + assert self.source_dir + if self.name: + logger.debug( + 'Running setup.py (path:%s) egg_info for package %s', + self.setup_py, self.name, + ) + else: + logger.debug( + 'Running setup.py (path:%s) egg_info for package from %s', + self.setup_py, self.link, + ) + + with indent_log(): + script = SETUPTOOLS_SHIM % self.setup_py + base_cmd = [sys.executable, '-c', script] + if self.isolated: + base_cmd += ["--no-user-cfg"] + egg_info_cmd = base_cmd + ['egg_info'] + # We can't put the .egg-info files at the root, because then the + # source code will be mistaken for an installed egg, causing + # problems + if self.editable: + egg_base_option = [] + else: + egg_info_dir = os.path.join(self.setup_py_dir, 'pip-egg-info') + ensure_dir(egg_info_dir) + egg_base_option = ['--egg-base', 'pip-egg-info'] + with self.build_env: + call_subprocess( + egg_info_cmd + egg_base_option, + cwd=self.setup_py_dir, + show_stdout=False, + command_desc='python setup.py egg_info') + + if not self.req: + if isinstance(parse_version(self.metadata["Version"]), Version): + op = "==" + else: + op = "===" + self.req = Requirement( + "".join([ + self.metadata["Name"], + op, + self.metadata["Version"], + ]) + ) + self._correct_build_location() + else: + metadata_name = canonicalize_name(self.metadata["Name"]) + if canonicalize_name(self.req.name) != metadata_name: + logger.warning( + 'Running setup.py (path:%s) egg_info for package %s ' + 'produced metadata for project name %s. Fix your ' + '#egg=%s fragments.', + self.setup_py, self.name, metadata_name, self.name + ) + self.req = Requirement(metadata_name) + + @property + def egg_info_path(self): + if self._egg_info_path is None: + if self.editable: + base = self.source_dir + else: + base = os.path.join(self.setup_py_dir, 'pip-egg-info') + filenames = os.listdir(base) + if self.editable: + filenames = [] + for root, dirs, files in os.walk(base): + for dir in vcs.dirnames: + if dir in dirs: + dirs.remove(dir) + # Iterate over a copy of ``dirs``, since mutating + # a list while iterating over it can cause trouble. + # (See https://github.com/pypa/pip/pull/462.) + for dir in list(dirs): + # Don't search in anything that looks like a virtualenv + # environment + if ( + os.path.lexists( + os.path.join(root, dir, 'bin', 'python') + ) or + os.path.exists( + os.path.join( + root, dir, 'Scripts', 'Python.exe' + ) + )): + dirs.remove(dir) + # Also don't search through tests + elif dir == 'test' or dir == 'tests': + dirs.remove(dir) + filenames.extend([os.path.join(root, dir) + for dir in dirs]) + filenames = [f for f in filenames if f.endswith('.egg-info')] + + if not filenames: + raise InstallationError( + "Files/directories not found in %s" % base + ) + # if we have more than one match, we pick the toplevel one. This + # can easily be the case if there is a dist folder which contains + # an extracted tarball for testing purposes. + if len(filenames) > 1: + filenames.sort( + key=lambda x: x.count(os.path.sep) + + (os.path.altsep and x.count(os.path.altsep) or 0) + ) + self._egg_info_path = os.path.join(base, filenames[0]) + return self._egg_info_path + + @property + def metadata(self): + if not hasattr(self, '_metadata'): + self._metadata = get_metadata(self.get_dist()) + + return self._metadata + + def get_dist(self): + """Return a pkg_resources.Distribution built from self.egg_info_path""" + egg_info = self.egg_info_path.rstrip(os.path.sep) + base_dir = os.path.dirname(egg_info) + metadata = pkg_resources.PathMetadata(base_dir, egg_info) + dist_name = os.path.splitext(os.path.basename(egg_info))[0] + return pkg_resources.Distribution( + os.path.dirname(egg_info), + project_name=dist_name, + metadata=metadata, + ) + + def assert_source_matches_version(self): + assert self.source_dir + version = self.metadata['version'] + if self.req.specifier and version not in self.req.specifier: + logger.warning( + 'Requested %s, but installing version %s', + self, + version, + ) + else: + logger.debug( + 'Source in %s has version %s, which satisfies requirement %s', + display_path(self.source_dir), + version, + self, + ) + + # For both source distributions and editables + def ensure_has_source_dir(self, parent_dir): + """Ensure that a source_dir is set. + + This will create a temporary build dir if the name of the requirement + isn't known yet. + + :param parent_dir: The ideal pip parent_dir for the source_dir. + Generally src_dir for editables and build_dir for sdists. + :return: self.source_dir + """ + if self.source_dir is None: + self.source_dir = self.build_location(parent_dir) + return self.source_dir + + # For editable installations + def install_editable(self, install_options, + global_options=(), prefix=None): + logger.info('Running setup.py develop for %s', self.name) + + if self.isolated: + global_options = list(global_options) + ["--no-user-cfg"] + + if prefix: + prefix_param = ['--prefix={}'.format(prefix)] + install_options = list(install_options) + prefix_param + + with indent_log(): + # FIXME: should we do --install-headers here too? + with self.build_env: + call_subprocess( + [ + sys.executable, + '-c', + SETUPTOOLS_SHIM % self.setup_py + ] + + list(global_options) + + ['develop', '--no-deps'] + + list(install_options), + + cwd=self.setup_py_dir, + show_stdout=False, + ) + + self.install_succeeded = True + + def update_editable(self, obtain=True): + if not self.link: + logger.debug( + "Cannot update repository at %s; repository location is " + "unknown", + self.source_dir, + ) + return + assert self.editable + assert self.source_dir + if self.link.scheme == 'file': + # Static paths don't get updated + return + assert '+' in self.link.url, "bad url: %r" % self.link.url + if not self.update: + return + vc_type, url = self.link.url.split('+', 1) + backend = vcs.get_backend(vc_type) + if backend: + vcs_backend = backend(self.link.url) + if obtain: + vcs_backend.obtain(self.source_dir) + else: + vcs_backend.export(self.source_dir) + else: + assert 0, ( + 'Unexpected version control type (in %s): %s' + % (self.link, vc_type)) + + # Top-level Actions + def uninstall(self, auto_confirm=False, verbose=False, + use_user_site=False): + """ + Uninstall the distribution currently satisfying this requirement. + + Prompts before removing or modifying files unless + ``auto_confirm`` is True. + + Refuses to delete or modify files outside of ``sys.prefix`` - + thus uninstallation within a virtual environment can only + modify that virtual environment, even if the virtualenv is + linked to global site-packages. + + """ + if not self.check_if_exists(use_user_site): + logger.warning("Skipping %s as it is not installed.", self.name) + return + dist = self.satisfied_by or self.conflicts_with + + uninstalled_pathset = UninstallPathSet.from_dist(dist) + uninstalled_pathset.remove(auto_confirm, verbose) + return uninstalled_pathset + + def _clean_zip_name(self, name, prefix): # only used by archive. + assert name.startswith(prefix + os.path.sep), ( + "name %r doesn't start with prefix %r" % (name, prefix) + ) + name = name[len(prefix) + 1:] + name = name.replace(os.path.sep, '/') + return name + + # TODO: Investigate if this should be kept in InstallRequirement + # Seems to be used only when VCS + downloads + def archive(self, build_dir): + assert self.source_dir + create_archive = True + archive_name = '%s-%s.zip' % (self.name, self.metadata["version"]) + archive_path = os.path.join(build_dir, archive_name) + if os.path.exists(archive_path): + response = ask_path_exists( + 'The file %s exists. (i)gnore, (w)ipe, (b)ackup, (a)bort ' % + display_path(archive_path), ('i', 'w', 'b', 'a')) + if response == 'i': + create_archive = False + elif response == 'w': + logger.warning('Deleting %s', display_path(archive_path)) + os.remove(archive_path) + elif response == 'b': + dest_file = backup_dir(archive_path) + logger.warning( + 'Backing up %s to %s', + display_path(archive_path), + display_path(dest_file), + ) + shutil.move(archive_path, dest_file) + elif response == 'a': + sys.exit(-1) + if create_archive: + zip = zipfile.ZipFile( + archive_path, 'w', zipfile.ZIP_DEFLATED, + allowZip64=True + ) + dir = os.path.normcase(os.path.abspath(self.setup_py_dir)) + for dirpath, dirnames, filenames in os.walk(dir): + if 'pip-egg-info' in dirnames: + dirnames.remove('pip-egg-info') + for dirname in dirnames: + dirname = os.path.join(dirpath, dirname) + name = self._clean_zip_name(dirname, dir) + zipdir = zipfile.ZipInfo(self.name + '/' + name + '/') + zipdir.external_attr = 0x1ED << 16 # 0o755 + zip.writestr(zipdir, '') + for filename in filenames: + if filename == PIP_DELETE_MARKER_FILENAME: + continue + filename = os.path.join(dirpath, filename) + name = self._clean_zip_name(filename, dir) + zip.write(filename, self.name + '/' + name) + zip.close() + logger.info('Saved %s', display_path(archive_path)) + + def install(self, install_options, global_options=None, root=None, + home=None, prefix=None, warn_script_location=True, + use_user_site=False, pycompile=True): + global_options = global_options if global_options is not None else [] + if self.editable: + self.install_editable( + install_options, global_options, prefix=prefix, + ) + return + if self.is_wheel: + version = wheel.wheel_version(self.source_dir) + wheel.check_compatibility(version, self.name) + + self.move_wheel_files( + self.source_dir, root=root, prefix=prefix, home=home, + warn_script_location=warn_script_location, + use_user_site=use_user_site, pycompile=pycompile, + ) + self.install_succeeded = True + return + + # Extend the list of global and install options passed on to + # the setup.py call with the ones from the requirements file. + # Options specified in requirements file override those + # specified on the command line, since the last option given + # to setup.py is the one that is used. + global_options = list(global_options) + \ + self.options.get('global_options', []) + install_options = list(install_options) + \ + self.options.get('install_options', []) + + if self.isolated: + global_options = global_options + ["--no-user-cfg"] + + with TempDirectory(kind="record") as temp_dir: + record_filename = os.path.join(temp_dir.path, 'install-record.txt') + install_args = self.get_install_args( + global_options, record_filename, root, prefix, pycompile, + ) + msg = 'Running setup.py install for %s' % (self.name,) + with open_spinner(msg) as spinner: + with indent_log(): + with self.build_env: + call_subprocess( + install_args + install_options, + cwd=self.setup_py_dir, + show_stdout=False, + spinner=spinner, + ) + + if not os.path.exists(record_filename): + logger.debug('Record file %s not found', record_filename) + return + self.install_succeeded = True + + def prepend_root(path): + if root is None or not os.path.isabs(path): + return path + else: + return change_root(root, path) + + with open(record_filename) as f: + for line in f: + directory = os.path.dirname(line) + if directory.endswith('.egg-info'): + egg_info_dir = prepend_root(directory) + break + else: + logger.warning( + 'Could not find .egg-info directory in install record' + ' for %s', + self, + ) + # FIXME: put the record somewhere + # FIXME: should this be an error? + return + new_lines = [] + with open(record_filename) as f: + for line in f: + filename = line.strip() + if os.path.isdir(filename): + filename += os.path.sep + new_lines.append( + os.path.relpath(prepend_root(filename), egg_info_dir) + ) + new_lines.sort() + ensure_dir(egg_info_dir) + inst_files_path = os.path.join(egg_info_dir, 'installed-files.txt') + with open(inst_files_path, 'w') as f: + f.write('\n'.join(new_lines) + '\n') + + def get_install_args(self, global_options, record_filename, root, prefix, + pycompile): + install_args = [sys.executable, "-u"] + install_args.append('-c') + install_args.append(SETUPTOOLS_SHIM % self.setup_py) + install_args += list(global_options) + \ + ['install', '--record', record_filename] + install_args += ['--single-version-externally-managed'] + + if root is not None: + install_args += ['--root', root] + if prefix is not None: + install_args += ['--prefix', prefix] + + if pycompile: + install_args += ["--compile"] + else: + install_args += ["--no-compile"] + + if running_under_virtualenv(): + py_ver_str = 'python' + sysconfig.get_python_version() + install_args += ['--install-headers', + os.path.join(sys.prefix, 'include', 'site', + py_ver_str, self.name)] + + return install_args diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/req_set.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/req_set.py new file mode 100644 index 0000000..b198317 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/req_set.py @@ -0,0 +1,181 @@ +from __future__ import absolute_import + +import logging +from collections import OrderedDict + +from pip._internal.exceptions import InstallationError +from pip._internal.utils.logging import indent_log +from pip._internal.wheel import Wheel + +logger = logging.getLogger(__name__) + + +class RequirementSet(object): + + def __init__(self, require_hashes=False, check_supported_wheels=True): + """Create a RequirementSet. + """ + + self.requirements = OrderedDict() + self.require_hashes = require_hashes + self.check_supported_wheels = check_supported_wheels + + # Mapping of alias: real_name + self.requirement_aliases = {} + self.unnamed_requirements = [] + self.successfully_downloaded = [] + self.reqs_to_cleanup = [] + + def __str__(self): + reqs = [req for req in self.requirements.values() + if not req.comes_from] + reqs.sort(key=lambda req: req.name.lower()) + return ' '.join([str(req.req) for req in reqs]) + + def __repr__(self): + reqs = [req for req in self.requirements.values()] + reqs.sort(key=lambda req: req.name.lower()) + reqs_str = ', '.join([str(req.req) for req in reqs]) + return ('<%s object; %d requirement(s): %s>' + % (self.__class__.__name__, len(reqs), reqs_str)) + + def add_requirement(self, install_req, parent_req_name=None, + extras_requested=None): + """Add install_req as a requirement to install. + + :param parent_req_name: The name of the requirement that needed this + added. The name is used because when multiple unnamed requirements + resolve to the same name, we could otherwise end up with dependency + links that point outside the Requirements set. parent_req must + already be added. Note that None implies that this is a user + supplied requirement, vs an inferred one. + :param extras_requested: an iterable of extras used to evaluate the + environment markers. + :return: Additional requirements to scan. That is either [] if + the requirement is not applicable, or [install_req] if the + requirement is applicable and has just been added. + """ + name = install_req.name + + # If the markers do not match, ignore this requirement. + if not install_req.match_markers(extras_requested): + logger.info( + "Ignoring %s: markers '%s' don't match your environment", + name, install_req.markers, + ) + return [], None + + # If the wheel is not supported, raise an error. + # Should check this after filtering out based on environment markers to + # allow specifying different wheels based on the environment/OS, in a + # single requirements file. + if install_req.link and install_req.link.is_wheel: + wheel = Wheel(install_req.link.filename) + if self.check_supported_wheels and not wheel.supported(): + raise InstallationError( + "%s is not a supported wheel on this platform." % + wheel.filename + ) + + # This next bit is really a sanity check. + assert install_req.is_direct == (parent_req_name is None), ( + "a direct req shouldn't have a parent and also, " + "a non direct req should have a parent" + ) + + # Unnamed requirements are scanned again and the requirement won't be + # added as a dependency until after scanning. + if not name: + # url or path requirement w/o an egg fragment + self.unnamed_requirements.append(install_req) + return [install_req], None + + try: + existing_req = self.get_requirement(name) + except KeyError: + existing_req = None + + has_conflicting_requirement = ( + parent_req_name is None and + existing_req and + not existing_req.constraint and + existing_req.extras == install_req.extras and + existing_req.req.specifier != install_req.req.specifier + ) + if has_conflicting_requirement: + raise InstallationError( + "Double requirement given: %s (already in %s, name=%r)" + % (install_req, existing_req, name) + ) + + # When no existing requirement exists, add the requirement as a + # dependency and it will be scanned again after. + if not existing_req: + self.requirements[name] = install_req + # FIXME: what about other normalizations? E.g., _ vs. -? + if name.lower() != name: + self.requirement_aliases[name.lower()] = name + # We'd want to rescan this requirements later + return [install_req], install_req + + # Assume there's no need to scan, and that we've already + # encountered this for scanning. + if install_req.constraint or not existing_req.constraint: + return [], existing_req + + does_not_satisfy_constraint = ( + install_req.link and + not ( + existing_req.link and + install_req.link.path == existing_req.link.path + ) + ) + if does_not_satisfy_constraint: + self.reqs_to_cleanup.append(install_req) + raise InstallationError( + "Could not satisfy constraints for '%s': " + "installation from path or url cannot be " + "constrained to a version" % name, + ) + # If we're now installing a constraint, mark the existing + # object for real installation. + existing_req.constraint = False + existing_req.extras = tuple(sorted( + set(existing_req.extras) | set(install_req.extras) + )) + logger.debug( + "Setting %s extras to: %s", + existing_req, existing_req.extras, + ) + # Return the existing requirement for addition to the parent and + # scanning again. + return [existing_req], existing_req + + def has_requirement(self, project_name): + name = project_name.lower() + if (name in self.requirements and + not self.requirements[name].constraint or + name in self.requirement_aliases and + not self.requirements[self.requirement_aliases[name]].constraint): + return True + return False + + @property + def has_requirements(self): + return list(req for req in self.requirements.values() if not + req.constraint) or self.unnamed_requirements + + def get_requirement(self, project_name): + for name in project_name, project_name.lower(): + if name in self.requirements: + return self.requirements[name] + if name in self.requirement_aliases: + return self.requirements[self.requirement_aliases[name]] + raise KeyError("No project with the name %r" % project_name) + + def cleanup_files(self): + """Clean up files, remove builds.""" + logger.debug('Cleaning up...') + with indent_log(): + for req in self.reqs_to_cleanup: + req.remove_temporary_source() diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/req_tracker.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/req_tracker.py new file mode 100644 index 0000000..0a86f4c --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/req_tracker.py @@ -0,0 +1,76 @@ +from __future__ import absolute_import + +import contextlib +import errno +import hashlib +import logging +import os + +from pip._internal.utils.temp_dir import TempDirectory + +logger = logging.getLogger(__name__) + + +class RequirementTracker(object): + + def __init__(self): + self._root = os.environ.get('PIP_REQ_TRACKER') + if self._root is None: + self._temp_dir = TempDirectory(delete=False, kind='req-tracker') + self._temp_dir.create() + self._root = os.environ['PIP_REQ_TRACKER'] = self._temp_dir.path + logger.debug('Created requirements tracker %r', self._root) + else: + self._temp_dir = None + logger.debug('Re-using requirements tracker %r', self._root) + self._entries = set() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.cleanup() + + def _entry_path(self, link): + hashed = hashlib.sha224(link.url_without_fragment.encode()).hexdigest() + return os.path.join(self._root, hashed) + + def add(self, req): + link = req.link + info = str(req) + entry_path = self._entry_path(link) + try: + with open(entry_path) as fp: + # Error, these's already a build in progress. + raise LookupError('%s is already being built: %s' + % (link, fp.read())) + except IOError as e: + if e.errno != errno.ENOENT: + raise + assert req not in self._entries + with open(entry_path, 'w') as fp: + fp.write(info) + self._entries.add(req) + logger.debug('Added %s to build tracker %r', req, self._root) + + def remove(self, req): + link = req.link + self._entries.remove(req) + os.unlink(self._entry_path(link)) + logger.debug('Removed %s from build tracker %r', req, self._root) + + def cleanup(self): + for req in set(self._entries): + self.remove(req) + remove = self._temp_dir is not None + if remove: + self._temp_dir.cleanup() + logger.debug('%s build tracker %r', + 'Removed' if remove else 'Cleaned', + self._root) + + @contextlib.contextmanager + def track(self, req): + self.add(req) + yield + self.remove(req) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/req_uninstall.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/req_uninstall.py new file mode 100644 index 0000000..a7d8230 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/req/req_uninstall.py @@ -0,0 +1,460 @@ +from __future__ import absolute_import + +import csv +import functools +import logging +import os +import sys +import sysconfig + +from pip._vendor import pkg_resources + +from pip._internal.exceptions import UninstallationError +from pip._internal.locations import bin_py, bin_user +from pip._internal.utils.compat import WINDOWS, cache_from_source, uses_pycache +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + FakeFile, ask, dist_in_usersite, dist_is_local, egg_link_path, is_local, + normalize_path, renames, +) +from pip._internal.utils.temp_dir import TempDirectory + +logger = logging.getLogger(__name__) + + +def _script_names(dist, script_name, is_gui): + """Create the fully qualified name of the files created by + {console,gui}_scripts for the given ``dist``. + Returns the list of file names + """ + if dist_in_usersite(dist): + bin_dir = bin_user + else: + bin_dir = bin_py + exe_name = os.path.join(bin_dir, script_name) + paths_to_remove = [exe_name] + if WINDOWS: + paths_to_remove.append(exe_name + '.exe') + paths_to_remove.append(exe_name + '.exe.manifest') + if is_gui: + paths_to_remove.append(exe_name + '-script.pyw') + else: + paths_to_remove.append(exe_name + '-script.py') + return paths_to_remove + + +def _unique(fn): + @functools.wraps(fn) + def unique(*args, **kw): + seen = set() + for item in fn(*args, **kw): + if item not in seen: + seen.add(item) + yield item + return unique + + +@_unique +def uninstallation_paths(dist): + """ + Yield all the uninstallation paths for dist based on RECORD-without-.py[co] + + Yield paths to all the files in RECORD. For each .py file in RECORD, add + the .pyc and .pyo in the same directory. + + UninstallPathSet.add() takes care of the __pycache__ .py[co]. + """ + r = csv.reader(FakeFile(dist.get_metadata_lines('RECORD'))) + for row in r: + path = os.path.join(dist.location, row[0]) + yield path + if path.endswith('.py'): + dn, fn = os.path.split(path) + base = fn[:-3] + path = os.path.join(dn, base + '.pyc') + yield path + path = os.path.join(dn, base + '.pyo') + yield path + + +def compact(paths): + """Compact a path set to contain the minimal number of paths + necessary to contain all paths in the set. If /a/path/ and + /a/path/to/a/file.txt are both in the set, leave only the + shorter path.""" + + sep = os.path.sep + short_paths = set() + for path in sorted(paths, key=len): + should_add = any( + path.startswith(shortpath.rstrip("*")) and + path[len(shortpath.rstrip("*").rstrip(sep))] == sep + for shortpath in short_paths + ) + if not should_add: + short_paths.add(path) + return short_paths + + +def compress_for_output_listing(paths): + """Returns a tuple of 2 sets of which paths to display to user + + The first set contains paths that would be deleted. Files of a package + are not added and the top-level directory of the package has a '*' added + at the end - to signify that all it's contents are removed. + + The second set contains files that would have been skipped in the above + folders. + """ + + will_remove = list(paths) + will_skip = set() + + # Determine folders and files + folders = set() + files = set() + for path in will_remove: + if path.endswith(".pyc"): + continue + if path.endswith("__init__.py") or ".dist-info" in path: + folders.add(os.path.dirname(path)) + files.add(path) + + _normcased_files = set(map(os.path.normcase, files)) + + folders = compact(folders) + + # This walks the tree using os.walk to not miss extra folders + # that might get added. + for folder in folders: + for dirpath, _, dirfiles in os.walk(folder): + for fname in dirfiles: + if fname.endswith(".pyc"): + continue + + file_ = os.path.join(dirpath, fname) + if (os.path.isfile(file_) and + os.path.normcase(file_) not in _normcased_files): + # We are skipping this file. Add it to the set. + will_skip.add(file_) + + will_remove = files | { + os.path.join(folder, "*") for folder in folders + } + + return will_remove, will_skip + + +class UninstallPathSet(object): + """A set of file paths to be removed in the uninstallation of a + requirement.""" + def __init__(self, dist): + self.paths = set() + self._refuse = set() + self.pth = {} + self.dist = dist + self.save_dir = TempDirectory(kind="uninstall") + self._moved_paths = [] + + def _permitted(self, path): + """ + Return True if the given path is one we are permitted to + remove/modify, False otherwise. + + """ + return is_local(path) + + def add(self, path): + head, tail = os.path.split(path) + + # we normalize the head to resolve parent directory symlinks, but not + # the tail, since we only want to uninstall symlinks, not their targets + path = os.path.join(normalize_path(head), os.path.normcase(tail)) + + if not os.path.exists(path): + return + if self._permitted(path): + self.paths.add(path) + else: + self._refuse.add(path) + + # __pycache__ files can show up after 'installed-files.txt' is created, + # due to imports + if os.path.splitext(path)[1] == '.py' and uses_pycache: + self.add(cache_from_source(path)) + + def add_pth(self, pth_file, entry): + pth_file = normalize_path(pth_file) + if self._permitted(pth_file): + if pth_file not in self.pth: + self.pth[pth_file] = UninstallPthEntries(pth_file) + self.pth[pth_file].add(entry) + else: + self._refuse.add(pth_file) + + def _stash(self, path): + return os.path.join( + self.save_dir.path, os.path.splitdrive(path)[1].lstrip(os.path.sep) + ) + + def remove(self, auto_confirm=False, verbose=False): + """Remove paths in ``self.paths`` with confirmation (unless + ``auto_confirm`` is True).""" + + if not self.paths: + logger.info( + "Can't uninstall '%s'. No files were found to uninstall.", + self.dist.project_name, + ) + return + + dist_name_version = ( + self.dist.project_name + "-" + self.dist.version + ) + logger.info('Uninstalling %s:', dist_name_version) + + with indent_log(): + if auto_confirm or self._allowed_to_proceed(verbose): + self.save_dir.create() + + for path in sorted(compact(self.paths)): + new_path = self._stash(path) + logger.debug('Removing file or directory %s', path) + self._moved_paths.append(path) + renames(path, new_path) + for pth in self.pth.values(): + pth.remove() + + logger.info('Successfully uninstalled %s', dist_name_version) + + def _allowed_to_proceed(self, verbose): + """Display which files would be deleted and prompt for confirmation + """ + + def _display(msg, paths): + if not paths: + return + + logger.info(msg) + with indent_log(): + for path in sorted(compact(paths)): + logger.info(path) + + if not verbose: + will_remove, will_skip = compress_for_output_listing(self.paths) + else: + # In verbose mode, display all the files that are going to be + # deleted. + will_remove = list(self.paths) + will_skip = set() + + _display('Would remove:', will_remove) + _display('Would not remove (might be manually added):', will_skip) + _display('Would not remove (outside of prefix):', self._refuse) + + return ask('Proceed (y/n)? ', ('y', 'n')) == 'y' + + def rollback(self): + """Rollback the changes previously made by remove().""" + if self.save_dir.path is None: + logger.error( + "Can't roll back %s; was not uninstalled", + self.dist.project_name, + ) + return False + logger.info('Rolling back uninstall of %s', self.dist.project_name) + for path in self._moved_paths: + tmp_path = self._stash(path) + logger.debug('Replacing %s', path) + renames(tmp_path, path) + for pth in self.pth.values(): + pth.rollback() + + def commit(self): + """Remove temporary save dir: rollback will no longer be possible.""" + self.save_dir.cleanup() + self._moved_paths = [] + + @classmethod + def from_dist(cls, dist): + dist_path = normalize_path(dist.location) + if not dist_is_local(dist): + logger.info( + "Not uninstalling %s at %s, outside environment %s", + dist.key, + dist_path, + sys.prefix, + ) + return cls(dist) + + if dist_path in {p for p in {sysconfig.get_path("stdlib"), + sysconfig.get_path("platstdlib")} + if p}: + logger.info( + "Not uninstalling %s at %s, as it is in the standard library.", + dist.key, + dist_path, + ) + return cls(dist) + + paths_to_remove = cls(dist) + develop_egg_link = egg_link_path(dist) + develop_egg_link_egg_info = '{}.egg-info'.format( + pkg_resources.to_filename(dist.project_name)) + egg_info_exists = dist.egg_info and os.path.exists(dist.egg_info) + # Special case for distutils installed package + distutils_egg_info = getattr(dist._provider, 'path', None) + + # Uninstall cases order do matter as in the case of 2 installs of the + # same package, pip needs to uninstall the currently detected version + if (egg_info_exists and dist.egg_info.endswith('.egg-info') and + not dist.egg_info.endswith(develop_egg_link_egg_info)): + # if dist.egg_info.endswith(develop_egg_link_egg_info), we + # are in fact in the develop_egg_link case + paths_to_remove.add(dist.egg_info) + if dist.has_metadata('installed-files.txt'): + for installed_file in dist.get_metadata( + 'installed-files.txt').splitlines(): + path = os.path.normpath( + os.path.join(dist.egg_info, installed_file) + ) + paths_to_remove.add(path) + # FIXME: need a test for this elif block + # occurs with --single-version-externally-managed/--record outside + # of pip + elif dist.has_metadata('top_level.txt'): + if dist.has_metadata('namespace_packages.txt'): + namespaces = dist.get_metadata('namespace_packages.txt') + else: + namespaces = [] + for top_level_pkg in [ + p for p + in dist.get_metadata('top_level.txt').splitlines() + if p and p not in namespaces]: + path = os.path.join(dist.location, top_level_pkg) + paths_to_remove.add(path) + paths_to_remove.add(path + '.py') + paths_to_remove.add(path + '.pyc') + paths_to_remove.add(path + '.pyo') + + elif distutils_egg_info: + raise UninstallationError( + "Cannot uninstall {!r}. It is a distutils installed project " + "and thus we cannot accurately determine which files belong " + "to it which would lead to only a partial uninstall.".format( + dist.project_name, + ) + ) + + elif dist.location.endswith('.egg'): + # package installed by easy_install + # We cannot match on dist.egg_name because it can slightly vary + # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg + paths_to_remove.add(dist.location) + easy_install_egg = os.path.split(dist.location)[1] + easy_install_pth = os.path.join(os.path.dirname(dist.location), + 'easy-install.pth') + paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg) + + elif egg_info_exists and dist.egg_info.endswith('.dist-info'): + for path in uninstallation_paths(dist): + paths_to_remove.add(path) + + elif develop_egg_link: + # develop egg + with open(develop_egg_link, 'r') as fh: + link_pointer = os.path.normcase(fh.readline().strip()) + assert (link_pointer == dist.location), ( + 'Egg-link %s does not match installed location of %s ' + '(at %s)' % (link_pointer, dist.project_name, dist.location) + ) + paths_to_remove.add(develop_egg_link) + easy_install_pth = os.path.join(os.path.dirname(develop_egg_link), + 'easy-install.pth') + paths_to_remove.add_pth(easy_install_pth, dist.location) + + else: + logger.debug( + 'Not sure how to uninstall: %s - Check: %s', + dist, dist.location, + ) + + # find distutils scripts= scripts + if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'): + for script in dist.metadata_listdir('scripts'): + if dist_in_usersite(dist): + bin_dir = bin_user + else: + bin_dir = bin_py + paths_to_remove.add(os.path.join(bin_dir, script)) + if WINDOWS: + paths_to_remove.add(os.path.join(bin_dir, script) + '.bat') + + # find console_scripts + _scripts_to_remove = [] + console_scripts = dist.get_entry_map(group='console_scripts') + for name in console_scripts.keys(): + _scripts_to_remove.extend(_script_names(dist, name, False)) + # find gui_scripts + gui_scripts = dist.get_entry_map(group='gui_scripts') + for name in gui_scripts.keys(): + _scripts_to_remove.extend(_script_names(dist, name, True)) + + for s in _scripts_to_remove: + paths_to_remove.add(s) + + return paths_to_remove + + +class UninstallPthEntries(object): + def __init__(self, pth_file): + if not os.path.isfile(pth_file): + raise UninstallationError( + "Cannot remove entries from nonexistent file %s" % pth_file + ) + self.file = pth_file + self.entries = set() + self._saved_lines = None + + def add(self, entry): + entry = os.path.normcase(entry) + # On Windows, os.path.normcase converts the entry to use + # backslashes. This is correct for entries that describe absolute + # paths outside of site-packages, but all the others use forward + # slashes. + if WINDOWS and not os.path.splitdrive(entry)[0]: + entry = entry.replace('\\', '/') + self.entries.add(entry) + + def remove(self): + logger.debug('Removing pth entries from %s:', self.file) + with open(self.file, 'rb') as fh: + # windows uses '\r\n' with py3k, but uses '\n' with py2.x + lines = fh.readlines() + self._saved_lines = lines + if any(b'\r\n' in line for line in lines): + endline = '\r\n' + else: + endline = '\n' + # handle missing trailing newline + if lines and not lines[-1].endswith(endline.encode("utf-8")): + lines[-1] = lines[-1] + endline.encode("utf-8") + for entry in self.entries: + try: + logger.debug('Removing entry: %s', entry) + lines.remove((entry + endline).encode("utf-8")) + except ValueError: + pass + with open(self.file, 'wb') as fh: + fh.writelines(lines) + + def rollback(self): + if self._saved_lines is None: + logger.error( + 'Cannot roll back changes to %s, none were made', self.file + ) + return False + logger.debug('Rolling %s back to previous state', self.file) + with open(self.file, 'wb') as fh: + fh.writelines(self._saved_lines) + return True diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/resolve.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/resolve.py new file mode 100644 index 0000000..2d9f1c5 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/resolve.py @@ -0,0 +1,353 @@ +"""Dependency Resolution + +The dependency resolution in pip is performed as follows: + +for top-level requirements: + a. only one spec allowed per project, regardless of conflicts or not. + otherwise a "double requirement" exception is raised + b. they override sub-dependency requirements. +for sub-dependencies + a. "first found, wins" (where the order is breadth first) +""" + +import logging +from collections import defaultdict +from itertools import chain + +from pip._internal.exceptions import ( + BestVersionAlreadyInstalled, DistributionNotFound, HashError, HashErrors, + UnsupportedPythonVersion, +) +from pip._internal.req.constructors import install_req_from_req +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import dist_in_usersite, ensure_dir +from pip._internal.utils.packaging import check_dist_requires_python + +logger = logging.getLogger(__name__) + + +class Resolver(object): + """Resolves which packages need to be installed/uninstalled to perform \ + the requested operation without breaking the requirements of any package. + """ + + _allowed_strategies = {"eager", "only-if-needed", "to-satisfy-only"} + + def __init__(self, preparer, session, finder, wheel_cache, use_user_site, + ignore_dependencies, ignore_installed, ignore_requires_python, + force_reinstall, isolated, upgrade_strategy): + super(Resolver, self).__init__() + assert upgrade_strategy in self._allowed_strategies + + self.preparer = preparer + self.finder = finder + self.session = session + + # NOTE: This would eventually be replaced with a cache that can give + # information about both sdist and wheels transparently. + self.wheel_cache = wheel_cache + + self.require_hashes = None # This is set in resolve + + self.upgrade_strategy = upgrade_strategy + self.force_reinstall = force_reinstall + self.isolated = isolated + self.ignore_dependencies = ignore_dependencies + self.ignore_installed = ignore_installed + self.ignore_requires_python = ignore_requires_python + self.use_user_site = use_user_site + + self._discovered_dependencies = defaultdict(list) + + def resolve(self, requirement_set): + """Resolve what operations need to be done + + As a side-effect of this method, the packages (and their dependencies) + are downloaded, unpacked and prepared for installation. This + preparation is done by ``pip.operations.prepare``. + + Once PyPI has static dependency metadata available, it would be + possible to move the preparation to become a step separated from + dependency resolution. + """ + # make the wheelhouse + if self.preparer.wheel_download_dir: + ensure_dir(self.preparer.wheel_download_dir) + + # If any top-level requirement has a hash specified, enter + # hash-checking mode, which requires hashes from all. + root_reqs = ( + requirement_set.unnamed_requirements + + list(requirement_set.requirements.values()) + ) + self.require_hashes = ( + requirement_set.require_hashes or + any(req.has_hash_options for req in root_reqs) + ) + + # Display where finder is looking for packages + locations = self.finder.get_formatted_locations() + if locations: + logger.info(locations) + + # Actually prepare the files, and collect any exceptions. Most hash + # exceptions cannot be checked ahead of time, because + # req.populate_link() needs to be called before we can make decisions + # based on link type. + discovered_reqs = [] + hash_errors = HashErrors() + for req in chain(root_reqs, discovered_reqs): + try: + discovered_reqs.extend( + self._resolve_one(requirement_set, req) + ) + except HashError as exc: + exc.req = req + hash_errors.append(exc) + + if hash_errors: + raise hash_errors + + def _is_upgrade_allowed(self, req): + if self.upgrade_strategy == "to-satisfy-only": + return False + elif self.upgrade_strategy == "eager": + return True + else: + assert self.upgrade_strategy == "only-if-needed" + return req.is_direct + + def _set_req_to_reinstall(self, req): + """ + Set a requirement to be installed. + """ + # Don't uninstall the conflict if doing a user install and the + # conflict is not a user install. + if not self.use_user_site or dist_in_usersite(req.satisfied_by): + req.conflicts_with = req.satisfied_by + req.satisfied_by = None + + # XXX: Stop passing requirement_set for options + def _check_skip_installed(self, req_to_install): + """Check if req_to_install should be skipped. + + This will check if the req is installed, and whether we should upgrade + or reinstall it, taking into account all the relevant user options. + + After calling this req_to_install will only have satisfied_by set to + None if the req_to_install is to be upgraded/reinstalled etc. Any + other value will be a dist recording the current thing installed that + satisfies the requirement. + + Note that for vcs urls and the like we can't assess skipping in this + routine - we simply identify that we need to pull the thing down, + then later on it is pulled down and introspected to assess upgrade/ + reinstalls etc. + + :return: A text reason for why it was skipped, or None. + """ + if self.ignore_installed: + return None + + req_to_install.check_if_exists(self.use_user_site) + if not req_to_install.satisfied_by: + return None + + if self.force_reinstall: + self._set_req_to_reinstall(req_to_install) + return None + + if not self._is_upgrade_allowed(req_to_install): + if self.upgrade_strategy == "only-if-needed": + return 'already satisfied, skipping upgrade' + return 'already satisfied' + + # Check for the possibility of an upgrade. For link-based + # requirements we have to pull the tree down and inspect to assess + # the version #, so it's handled way down. + if not req_to_install.link: + try: + self.finder.find_requirement(req_to_install, upgrade=True) + except BestVersionAlreadyInstalled: + # Then the best version is installed. + return 'already up-to-date' + except DistributionNotFound: + # No distribution found, so we squash the error. It will + # be raised later when we re-try later to do the install. + # Why don't we just raise here? + pass + + self._set_req_to_reinstall(req_to_install) + return None + + def _get_abstract_dist_for(self, req): + """Takes a InstallRequirement and returns a single AbstractDist \ + representing a prepared variant of the same. + """ + assert self.require_hashes is not None, ( + "require_hashes should have been set in Resolver.resolve()" + ) + + if req.editable: + return self.preparer.prepare_editable_requirement( + req, self.require_hashes, self.use_user_site, self.finder, + ) + + # satisfied_by is only evaluated by calling _check_skip_installed, + # so it must be None here. + assert req.satisfied_by is None + skip_reason = self._check_skip_installed(req) + + if req.satisfied_by: + return self.preparer.prepare_installed_requirement( + req, self.require_hashes, skip_reason + ) + + upgrade_allowed = self._is_upgrade_allowed(req) + abstract_dist = self.preparer.prepare_linked_requirement( + req, self.session, self.finder, upgrade_allowed, + self.require_hashes + ) + + # NOTE + # The following portion is for determining if a certain package is + # going to be re-installed/upgraded or not and reporting to the user. + # This should probably get cleaned up in a future refactor. + + # req.req is only avail after unpack for URL + # pkgs repeat check_if_exists to uninstall-on-upgrade + # (#14) + if not self.ignore_installed: + req.check_if_exists(self.use_user_site) + + if req.satisfied_by: + should_modify = ( + self.upgrade_strategy != "to-satisfy-only" or + self.force_reinstall or + self.ignore_installed or + req.link.scheme == 'file' + ) + if should_modify: + self._set_req_to_reinstall(req) + else: + logger.info( + 'Requirement already satisfied (use --upgrade to upgrade):' + ' %s', req, + ) + + return abstract_dist + + def _resolve_one(self, requirement_set, req_to_install): + """Prepare a single requirements file. + + :return: A list of additional InstallRequirements to also install. + """ + # Tell user what we are doing for this requirement: + # obtain (editable), skipping, processing (local url), collecting + # (remote url or package name) + if req_to_install.constraint or req_to_install.prepared: + return [] + + req_to_install.prepared = True + + # register tmp src for cleanup in case something goes wrong + requirement_set.reqs_to_cleanup.append(req_to_install) + + abstract_dist = self._get_abstract_dist_for(req_to_install) + + # Parse and return dependencies + dist = abstract_dist.dist(self.finder) + try: + check_dist_requires_python(dist) + except UnsupportedPythonVersion as err: + if self.ignore_requires_python: + logger.warning(err.args[0]) + else: + raise + + more_reqs = [] + + def add_req(subreq, extras_requested): + sub_install_req = install_req_from_req( + str(subreq), + req_to_install, + isolated=self.isolated, + wheel_cache=self.wheel_cache, + ) + parent_req_name = req_to_install.name + to_scan_again, add_to_parent = requirement_set.add_requirement( + sub_install_req, + parent_req_name=parent_req_name, + extras_requested=extras_requested, + ) + if parent_req_name and add_to_parent: + self._discovered_dependencies[parent_req_name].append( + add_to_parent + ) + more_reqs.extend(to_scan_again) + + with indent_log(): + # We add req_to_install before its dependencies, so that we + # can refer to it when adding dependencies. + if not requirement_set.has_requirement(req_to_install.name): + # 'unnamed' requirements will get added here + req_to_install.is_direct = True + requirement_set.add_requirement( + req_to_install, parent_req_name=None, + ) + + if not self.ignore_dependencies: + if req_to_install.extras: + logger.debug( + "Installing extra requirements: %r", + ','.join(req_to_install.extras), + ) + missing_requested = sorted( + set(req_to_install.extras) - set(dist.extras) + ) + for missing in missing_requested: + logger.warning( + '%s does not provide the extra \'%s\'', + dist, missing + ) + + available_requested = sorted( + set(dist.extras) & set(req_to_install.extras) + ) + for subreq in dist.requires(available_requested): + add_req(subreq, extras_requested=available_requested) + + if not req_to_install.editable and not req_to_install.satisfied_by: + # XXX: --no-install leads this to report 'Successfully + # downloaded' for only non-editable reqs, even though we took + # action on them. + requirement_set.successfully_downloaded.append(req_to_install) + + return more_reqs + + def get_installation_order(self, req_set): + """Create the installation order. + + The installation order is topological - requirements are installed + before the requiring thing. We break cycles at an arbitrary point, + and make no other guarantees. + """ + # The current implementation, which we may change at any point + # installs the user specified things in the order given, except when + # dependencies must come earlier to achieve topological order. + order = [] + ordered_reqs = set() + + def schedule(req): + if req.satisfied_by or req in ordered_reqs: + return + if req.constraint: + return + ordered_reqs.add(req) + for dep in self._discovered_dependencies[req.name]: + schedule(dep) + order.append(req) + + for install_req in req_set.requirements.values(): + schedule(install_req) + return order diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/__init__.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/appdirs.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/appdirs.py new file mode 100644 index 0000000..cc96f98 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/appdirs.py @@ -0,0 +1,258 @@ +""" +This code was taken from https://github.com/ActiveState/appdirs and modified +to suit our purposes. +""" +from __future__ import absolute_import + +import os +import sys + +from pip._vendor.six import PY2, text_type + +from pip._internal.utils.compat import WINDOWS, expanduser + + +def user_cache_dir(appname): + r""" + Return full path to the user-specific cache dir for this application. + + "appname" is the name of application. + + Typical user cache directories are: + macOS: ~/Library/Caches/ + Unix: ~/.cache/ (XDG default) + Windows: C:\Users\\AppData\Local\\Cache + + On Windows the only suggestion in the MSDN docs is that local settings go + in the `CSIDL_LOCAL_APPDATA` directory. This is identical to the + non-roaming app data dir (the default returned by `user_data_dir`). Apps + typically put cache data somewhere *under* the given dir here. Some + examples: + ...\Mozilla\Firefox\Profiles\\Cache + ...\Acme\SuperApp\Cache\1.0 + + OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. + """ + if WINDOWS: + # Get the base path + path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) + + # When using Python 2, return paths as bytes on Windows like we do on + # other operating systems. See helper function docs for more details. + if PY2 and isinstance(path, text_type): + path = _win_path_to_bytes(path) + + # Add our app name and Cache directory to it + path = os.path.join(path, appname, "Cache") + elif sys.platform == "darwin": + # Get the base path + path = expanduser("~/Library/Caches") + + # Add our app name to it + path = os.path.join(path, appname) + else: + # Get the base path + path = os.getenv("XDG_CACHE_HOME", expanduser("~/.cache")) + + # Add our app name to it + path = os.path.join(path, appname) + + return path + + +def user_data_dir(appname, roaming=False): + r""" + Return full path to the user-specific data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + + Typical user data directories are: + macOS: ~/Library/Application Support/ + if it exists, else ~/.config/ + Unix: ~/.local/share/ # or in + $XDG_DATA_HOME, if defined + Win XP (not roaming): C:\Documents and Settings\\ ... + ...Application Data\ + Win XP (roaming): C:\Documents and Settings\\Local ... + ...Settings\Application Data\ + Win 7 (not roaming): C:\\Users\\AppData\Local\ + Win 7 (roaming): C:\\Users\\AppData\Roaming\ + + For Unix, we follow the XDG spec and support $XDG_DATA_HOME. + That means, by default "~/.local/share/". + """ + if WINDOWS: + const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" + path = os.path.join(os.path.normpath(_get_win_folder(const)), appname) + elif sys.platform == "darwin": + path = os.path.join( + expanduser('~/Library/Application Support/'), + appname, + ) if os.path.isdir(os.path.join( + expanduser('~/Library/Application Support/'), + appname, + ) + ) else os.path.join( + expanduser('~/.config/'), + appname, + ) + else: + path = os.path.join( + os.getenv('XDG_DATA_HOME', expanduser("~/.local/share")), + appname, + ) + + return path + + +def user_config_dir(appname, roaming=True): + """Return full path to the user-specific config dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "roaming" (boolean, default True) can be set False to not use the + Windows roaming appdata directory. That means that for users on a + Windows network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + + Typical user data directories are: + macOS: same as user_data_dir + Unix: ~/.config/ + Win *: same as user_data_dir + + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. + That means, by default "~/.config/". + """ + if WINDOWS: + path = user_data_dir(appname, roaming=roaming) + elif sys.platform == "darwin": + path = user_data_dir(appname) + else: + path = os.getenv('XDG_CONFIG_HOME', expanduser("~/.config")) + path = os.path.join(path, appname) + + return path + + +# for the discussion regarding site_config_dirs locations +# see +def site_config_dirs(appname): + r"""Return a list of potential user-shared config dirs for this application. + + "appname" is the name of application. + + Typical user config directories are: + macOS: /Library/Application Support// + Unix: /etc or $XDG_CONFIG_DIRS[i]// for each value in + $XDG_CONFIG_DIRS + Win XP: C:\Documents and Settings\All Users\Application ... + ...Data\\ + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory + on Vista.) + Win 7: Hidden, but writeable on Win 7: + C:\ProgramData\\ + """ + if WINDOWS: + path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) + pathlist = [os.path.join(path, appname)] + elif sys.platform == 'darwin': + pathlist = [os.path.join('/Library/Application Support', appname)] + else: + # try looking in $XDG_CONFIG_DIRS + xdg_config_dirs = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') + if xdg_config_dirs: + pathlist = [ + os.path.join(expanduser(x), appname) + for x in xdg_config_dirs.split(os.pathsep) + ] + else: + pathlist = [] + + # always look in /etc directly as well + pathlist.append('/etc') + + return pathlist + + +# -- Windows support functions -- + +def _get_win_folder_from_registry(csidl_name): + """ + This is a fallback technique at best. I'm not sure if using the + registry for this guarantees us the correct answer for all CSIDL_* + names. + """ + import _winreg + + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + }[csidl_name] + + key = _winreg.OpenKey( + _winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" + ) + directory, _type = _winreg.QueryValueEx(key, shell_folder_name) + return directory + + +def _get_win_folder_with_ctypes(csidl_name): + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + }[csidl_name] + + buf = ctypes.create_unicode_buffer(1024) + ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in buf: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf2 = ctypes.create_unicode_buffer(1024) + if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + return buf.value + + +if WINDOWS: + try: + import ctypes + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + _get_win_folder = _get_win_folder_from_registry + + +def _win_path_to_bytes(path): + """Encode Windows paths to bytes. Only used on Python 2. + + Motivation is to be consistent with other operating systems where paths + are also returned as bytes. This avoids problems mixing bytes and Unicode + elsewhere in the codebase. For more details and discussion see + . + + If encoding using ASCII and MBCS fails, return the original Unicode path. + """ + for encoding in ('ASCII', 'MBCS'): + try: + return path.encode(encoding) + except (UnicodeEncodeError, LookupError): + pass + return path diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/compat.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/compat.py new file mode 100644 index 0000000..3114f2d --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/compat.py @@ -0,0 +1,248 @@ +"""Stuff that differs in different Python versions and platform +distributions.""" +from __future__ import absolute_import, division + +import codecs +import locale +import logging +import os +import shutil +import sys + +from pip._vendor.six import text_type + +try: + import ipaddress +except ImportError: + try: + from pip._vendor import ipaddress # type: ignore + except ImportError: + import ipaddr as ipaddress # type: ignore + ipaddress.ip_address = ipaddress.IPAddress + ipaddress.ip_network = ipaddress.IPNetwork + + +__all__ = [ + "ipaddress", "uses_pycache", "console_to_str", "native_str", + "get_path_uid", "stdlib_pkgs", "WINDOWS", "samefile", "get_terminal_size", + "get_extension_suffixes", +] + + +logger = logging.getLogger(__name__) + +if sys.version_info >= (3, 4): + uses_pycache = True + from importlib.util import cache_from_source +else: + import imp + + try: + cache_from_source = imp.cache_from_source # type: ignore + except AttributeError: + # does not use __pycache__ + cache_from_source = None + + uses_pycache = cache_from_source is not None + + +if sys.version_info >= (3, 5): + backslashreplace_decode = "backslashreplace" +else: + # In version 3.4 and older, backslashreplace exists + # but does not support use for decoding. + # We implement our own replace handler for this + # situation, so that we can consistently use + # backslash replacement for all versions. + def backslashreplace_decode_fn(err): + raw_bytes = (err.object[i] for i in range(err.start, err.end)) + if sys.version_info[0] == 2: + # Python 2 gave us characters - convert to numeric bytes + raw_bytes = (ord(b) for b in raw_bytes) + return u"".join(u"\\x%x" % c for c in raw_bytes), err.end + codecs.register_error( + "backslashreplace_decode", + backslashreplace_decode_fn, + ) + backslashreplace_decode = "backslashreplace_decode" + + +def console_to_str(data): + """Return a string, safe for output, of subprocess output. + + We assume the data is in the locale preferred encoding. + If it won't decode properly, we warn the user but decode as + best we can. + + We also ensure that the output can be safely written to + standard output without encoding errors. + """ + + # First, get the encoding we assume. This is the preferred + # encoding for the locale, unless that is not found, or + # it is ASCII, in which case assume UTF-8 + encoding = locale.getpreferredencoding() + if (not encoding) or codecs.lookup(encoding).name == "ascii": + encoding = "utf-8" + + # Now try to decode the data - if we fail, warn the user and + # decode with replacement. + try: + s = data.decode(encoding) + except UnicodeDecodeError: + logger.warning( + "Subprocess output does not appear to be encoded as %s", + encoding, + ) + s = data.decode(encoding, errors=backslashreplace_decode) + + # Make sure we can print the output, by encoding it to the output + # encoding with replacement of unencodable characters, and then + # decoding again. + # We use stderr's encoding because it's less likely to be + # redirected and if we don't find an encoding we skip this + # step (on the assumption that output is wrapped by something + # that won't fail). + # The double getattr is to deal with the possibility that we're + # being called in a situation where sys.__stderr__ doesn't exist, + # or doesn't have an encoding attribute. Neither of these cases + # should occur in normal pip use, but there's no harm in checking + # in case people use pip in (unsupported) unusual situations. + output_encoding = getattr(getattr(sys, "__stderr__", None), + "encoding", None) + + if output_encoding: + s = s.encode(output_encoding, errors="backslashreplace") + s = s.decode(output_encoding) + + return s + + +if sys.version_info >= (3,): + def native_str(s, replace=False): + if isinstance(s, bytes): + return s.decode('utf-8', 'replace' if replace else 'strict') + return s + +else: + def native_str(s, replace=False): + # Replace is ignored -- unicode to UTF-8 can't fail + if isinstance(s, text_type): + return s.encode('utf-8') + return s + + +def get_path_uid(path): + """ + Return path's uid. + + Does not follow symlinks: + https://github.com/pypa/pip/pull/935#discussion_r5307003 + + Placed this function in compat due to differences on AIX and + Jython, that should eventually go away. + + :raises OSError: When path is a symlink or can't be read. + """ + if hasattr(os, 'O_NOFOLLOW'): + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) + file_uid = os.fstat(fd).st_uid + os.close(fd) + else: # AIX and Jython + # WARNING: time of check vulnerability, but best we can do w/o NOFOLLOW + if not os.path.islink(path): + # older versions of Jython don't have `os.fstat` + file_uid = os.stat(path).st_uid + else: + # raise OSError for parity with os.O_NOFOLLOW above + raise OSError( + "%s is a symlink; Will not return uid for symlinks" % path + ) + return file_uid + + +if sys.version_info >= (3, 4): + from importlib.machinery import EXTENSION_SUFFIXES + + def get_extension_suffixes(): + return EXTENSION_SUFFIXES +else: + from imp import get_suffixes + + def get_extension_suffixes(): + return [suffix[0] for suffix in get_suffixes()] + + +def expanduser(path): + """ + Expand ~ and ~user constructions. + + Includes a workaround for https://bugs.python.org/issue14768 + """ + expanded = os.path.expanduser(path) + if path.startswith('~/') and expanded.startswith('//'): + expanded = expanded[1:] + return expanded + + +# packages in the stdlib that may have installation metadata, but should not be +# considered 'installed'. this theoretically could be determined based on +# dist.location (py27:`sysconfig.get_paths()['stdlib']`, +# py26:sysconfig.get_config_vars('LIBDEST')), but fear platform variation may +# make this ineffective, so hard-coding +stdlib_pkgs = {"python", "wsgiref", "argparse"} + + +# windows detection, covers cpython and ironpython +WINDOWS = (sys.platform.startswith("win") or + (sys.platform == 'cli' and os.name == 'nt')) + + +def samefile(file1, file2): + """Provide an alternative for os.path.samefile on Windows/Python2""" + if hasattr(os.path, 'samefile'): + return os.path.samefile(file1, file2) + else: + path1 = os.path.normcase(os.path.abspath(file1)) + path2 = os.path.normcase(os.path.abspath(file2)) + return path1 == path2 + + +if hasattr(shutil, 'get_terminal_size'): + def get_terminal_size(): + """ + Returns a tuple (x, y) representing the width(x) and the height(y) + in characters of the terminal window. + """ + return tuple(shutil.get_terminal_size()) +else: + def get_terminal_size(): + """ + Returns a tuple (x, y) representing the width(x) and the height(y) + in characters of the terminal window. + """ + def ioctl_GWINSZ(fd): + try: + import fcntl + import termios + import struct + cr = struct.unpack_from( + 'hh', + fcntl.ioctl(fd, termios.TIOCGWINSZ, '12345678') + ) + except Exception: + return None + if cr == (0, 0): + return None + return cr + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + if not cr: + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + cr = ioctl_GWINSZ(fd) + os.close(fd) + except Exception: + pass + if not cr: + cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) + return int(cr[1]), int(cr[0]) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/deprecation.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/deprecation.py new file mode 100644 index 0000000..bd744cf --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/deprecation.py @@ -0,0 +1,89 @@ +""" +A module that implements tooling to enable easy warnings about deprecations. +""" +from __future__ import absolute_import + +import logging +import warnings + +from pip._vendor.packaging.version import parse + +from pip import __version__ as current_version +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Optional # noqa: F401 + + +class PipDeprecationWarning(Warning): + pass + + +_original_showwarning = None # type: Any + + +# Warnings <-> Logging Integration +def _showwarning(message, category, filename, lineno, file=None, line=None): + if file is not None: + if _original_showwarning is not None: + _original_showwarning( + message, category, filename, lineno, file, line, + ) + elif issubclass(category, PipDeprecationWarning): + # We use a specially named logger which will handle all of the + # deprecation messages for pip. + logger = logging.getLogger("pip._internal.deprecations") + logger.warning(message) + else: + _original_showwarning( + message, category, filename, lineno, file, line, + ) + + +def install_warning_logger(): + # Enable our Deprecation Warnings + warnings.simplefilter("default", PipDeprecationWarning, append=True) + + global _original_showwarning + + if _original_showwarning is None: + _original_showwarning = warnings.showwarning + warnings.showwarning = _showwarning + + +def deprecated(reason, replacement, gone_in, issue=None): + # type: (str, Optional[str], Optional[str], Optional[int]) -> None + """Helper to deprecate existing functionality. + + reason: + Textual reason shown to the user about why this functionality has + been deprecated. + replacement: + Textual suggestion shown to the user about what alternative + functionality they can use. + gone_in: + The version of pip does this functionality should get removed in. + Raises errors if pip's current version is greater than or equal to + this. + issue: + Issue number on the tracker that would serve as a useful place for + users to find related discussion and provide feedback. + + Always pass replacement, gone_in and issue as keyword arguments for clarity + at the call site. + """ + + # Construct a nice message. + # This is purposely eagerly formatted as we want it to appear as if someone + # typed this entire message out. + message = "DEPRECATION: " + reason + if replacement is not None: + message += " A possible replacement is {}.".format(replacement) + if issue is not None: + url = "https://github.com/pypa/pip/issues/" + str(issue) + message += " You can find discussion regarding this at {}.".format(url) + + # Raise as an error if it has to be removed. + if gone_in is not None and parse(current_version) >= parse(gone_in): + raise PipDeprecationWarning(message) + warnings.warn(message, category=PipDeprecationWarning, stacklevel=2) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/encoding.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/encoding.py new file mode 100644 index 0000000..56f6036 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/encoding.py @@ -0,0 +1,33 @@ +import codecs +import locale +import re +import sys + +BOMS = [ + (codecs.BOM_UTF8, 'utf8'), + (codecs.BOM_UTF16, 'utf16'), + (codecs.BOM_UTF16_BE, 'utf16-be'), + (codecs.BOM_UTF16_LE, 'utf16-le'), + (codecs.BOM_UTF32, 'utf32'), + (codecs.BOM_UTF32_BE, 'utf32-be'), + (codecs.BOM_UTF32_LE, 'utf32-le'), +] + +ENCODING_RE = re.compile(br'coding[:=]\s*([-\w.]+)') + + +def auto_decode(data): + """Check a bytes string for a BOM to correctly detect the encoding + + Fallback to locale.getpreferredencoding(False) like open() on Python3""" + for bom, encoding in BOMS: + if data.startswith(bom): + return data[len(bom):].decode(encoding) + # Lets check the first two lines as in PEP263 + for line in data.split(b'\n')[:2]: + if line[0:1] == b'#' and ENCODING_RE.search(line): + encoding = ENCODING_RE.search(line).groups()[0].decode('ascii') + return data.decode(encoding) + return data.decode( + locale.getpreferredencoding(False) or sys.getdefaultencoding(), + ) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/filesystem.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/filesystem.py new file mode 100644 index 0000000..1e9cebd --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/filesystem.py @@ -0,0 +1,28 @@ +import os +import os.path + +from pip._internal.utils.compat import get_path_uid + + +def check_path_owner(path): + # If we don't have a way to check the effective uid of this process, then + # we'll just assume that we own the directory. + if not hasattr(os, "geteuid"): + return True + + previous = None + while path != previous: + if os.path.lexists(path): + # Check if path is writable by current user. + if os.geteuid() == 0: + # Special handling for root user in order to handle properly + # cases where users use sudo without -H flag. + try: + path_uid = get_path_uid(path) + except OSError: + return False + return path_uid == 0 + else: + return os.access(path, os.W_OK) + else: + previous, path = path, os.path.dirname(path) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/glibc.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/glibc.py new file mode 100644 index 0000000..ebcfc5b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/glibc.py @@ -0,0 +1,84 @@ +from __future__ import absolute_import + +import ctypes +import re +import warnings + + +def glibc_version_string(): + "Returns glibc version string, or None if not using glibc." + + # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen + # manpage says, "If filename is NULL, then the returned handle is for the + # main program". This way we can let the linker do the work to figure out + # which libc our process is actually using. + process_namespace = ctypes.CDLL(None) + try: + gnu_get_libc_version = process_namespace.gnu_get_libc_version + except AttributeError: + # Symbol doesn't exist -> therefore, we are not linked to + # glibc. + return None + + # Call gnu_get_libc_version, which returns a string like "2.5" + gnu_get_libc_version.restype = ctypes.c_char_p + version_str = gnu_get_libc_version() + # py2 / py3 compatibility: + if not isinstance(version_str, str): + version_str = version_str.decode("ascii") + + return version_str + + +# Separated out from have_compatible_glibc for easier unit testing +def check_glibc_version(version_str, required_major, minimum_minor): + # Parse string and check against requested version. + # + # We use a regexp instead of str.split because we want to discard any + # random junk that might come after the minor version -- this might happen + # in patched/forked versions of glibc (e.g. Linaro's version of glibc + # uses version strings like "2.20-2014.11"). See gh-3588. + m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) + if not m: + warnings.warn("Expected glibc version with 2 components major.minor," + " got: %s" % version_str, RuntimeWarning) + return False + return (int(m.group("major")) == required_major and + int(m.group("minor")) >= minimum_minor) + + +def have_compatible_glibc(required_major, minimum_minor): + version_str = glibc_version_string() + if version_str is None: + return False + return check_glibc_version(version_str, required_major, minimum_minor) + + +# platform.libc_ver regularly returns completely nonsensical glibc +# versions. E.g. on my computer, platform says: +# +# ~$ python2.7 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.7') +# ~$ python3.5 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.9') +# +# But the truth is: +# +# ~$ ldd --version +# ldd (Debian GLIBC 2.22-11) 2.22 +# +# This is unfortunate, because it means that the linehaul data on libc +# versions that was generated by pip 8.1.2 and earlier is useless and +# misleading. Solution: instead of using platform, use our code that actually +# works. +def libc_ver(): + """Try to determine the glibc version + + Returns a tuple of strings (lib, version) which default to empty strings + in case the lookup fails. + """ + glibc_version = glibc_version_string() + if glibc_version is None: + return ("", "") + else: + return ("glibc", glibc_version) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/hashes.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/hashes.py new file mode 100644 index 0000000..8b909ba --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/hashes.py @@ -0,0 +1,94 @@ +from __future__ import absolute_import + +import hashlib + +from pip._vendor.six import iteritems, iterkeys, itervalues + +from pip._internal.exceptions import ( + HashMismatch, HashMissing, InstallationError, +) +from pip._internal.utils.misc import read_chunks + +# The recommended hash algo of the moment. Change this whenever the state of +# the art changes; it won't hurt backward compatibility. +FAVORITE_HASH = 'sha256' + + +# Names of hashlib algorithms allowed by the --hash option and ``pip hash`` +# Currently, those are the ones at least as collision-resistant as sha256. +STRONG_HASHES = ['sha256', 'sha384', 'sha512'] + + +class Hashes(object): + """A wrapper that builds multiple hashes at once and checks them against + known-good values + + """ + def __init__(self, hashes=None): + """ + :param hashes: A dict of algorithm names pointing to lists of allowed + hex digests + """ + self._allowed = {} if hashes is None else hashes + + def check_against_chunks(self, chunks): + """Check good hashes against ones built from iterable of chunks of + data. + + Raise HashMismatch if none match. + + """ + gots = {} + for hash_name in iterkeys(self._allowed): + try: + gots[hash_name] = hashlib.new(hash_name) + except (ValueError, TypeError): + raise InstallationError('Unknown hash name: %s' % hash_name) + + for chunk in chunks: + for hash in itervalues(gots): + hash.update(chunk) + + for hash_name, got in iteritems(gots): + if got.hexdigest() in self._allowed[hash_name]: + return + self._raise(gots) + + def _raise(self, gots): + raise HashMismatch(self._allowed, gots) + + def check_against_file(self, file): + """Check good hashes against a file-like object + + Raise HashMismatch if none match. + + """ + return self.check_against_chunks(read_chunks(file)) + + def check_against_path(self, path): + with open(path, 'rb') as file: + return self.check_against_file(file) + + def __nonzero__(self): + """Return whether I know any known-good hashes.""" + return bool(self._allowed) + + def __bool__(self): + return self.__nonzero__() + + +class MissingHashes(Hashes): + """A workalike for Hashes used when we're missing a hash for a requirement + + It computes the actual hash of the requirement and raises a HashMissing + exception showing it to the user. + + """ + def __init__(self): + """Don't offer the ``hashes`` kwarg.""" + # Pass our favorite hash in to generate a "gotten hash". With the + # empty list, it will never match, so an error will always raise. + super(MissingHashes, self).__init__(hashes={FAVORITE_HASH: []}) + + def _raise(self, gots): + raise HashMissing(gots[FAVORITE_HASH].hexdigest()) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/logging.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/logging.py new file mode 100644 index 0000000..d9b9541 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/logging.py @@ -0,0 +1,225 @@ +from __future__ import absolute_import + +import contextlib +import logging +import logging.handlers +import os + +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.misc import ensure_dir + +try: + import threading +except ImportError: + import dummy_threading as threading # type: ignore + + +try: + from pip._vendor import colorama +# Lots of different errors can come from this, including SystemError and +# ImportError. +except Exception: + colorama = None + + +_log_state = threading.local() +_log_state.indentation = 0 + + +@contextlib.contextmanager +def indent_log(num=2): + """ + A context manager which will cause the log output to be indented for any + log messages emitted inside it. + """ + _log_state.indentation += num + try: + yield + finally: + _log_state.indentation -= num + + +def get_indentation(): + return getattr(_log_state, 'indentation', 0) + + +class IndentingFormatter(logging.Formatter): + + def format(self, record): + """ + Calls the standard formatter, but will indent all of the log messages + by our current indentation level. + """ + formatted = logging.Formatter.format(self, record) + formatted = "".join([ + (" " * get_indentation()) + line + for line in formatted.splitlines(True) + ]) + return formatted + + +def _color_wrap(*colors): + def wrapped(inp): + return "".join(list(colors) + [inp, colorama.Style.RESET_ALL]) + return wrapped + + +class ColorizedStreamHandler(logging.StreamHandler): + + # Don't build up a list of colors if we don't have colorama + if colorama: + COLORS = [ + # This needs to be in order from highest logging level to lowest. + (logging.ERROR, _color_wrap(colorama.Fore.RED)), + (logging.WARNING, _color_wrap(colorama.Fore.YELLOW)), + ] + else: + COLORS = [] + + def __init__(self, stream=None, no_color=None): + logging.StreamHandler.__init__(self, stream) + self._no_color = no_color + + if WINDOWS and colorama: + self.stream = colorama.AnsiToWin32(self.stream) + + def should_color(self): + # Don't colorize things if we do not have colorama or if told not to + if not colorama or self._no_color: + return False + + real_stream = ( + self.stream if not isinstance(self.stream, colorama.AnsiToWin32) + else self.stream.wrapped + ) + + # If the stream is a tty we should color it + if hasattr(real_stream, "isatty") and real_stream.isatty(): + return True + + # If we have an ANSI term we should color it + if os.environ.get("TERM") == "ANSI": + return True + + # If anything else we should not color it + return False + + def format(self, record): + msg = logging.StreamHandler.format(self, record) + + if self.should_color(): + for level, color in self.COLORS: + if record.levelno >= level: + msg = color(msg) + break + + return msg + + +class BetterRotatingFileHandler(logging.handlers.RotatingFileHandler): + + def _open(self): + ensure_dir(os.path.dirname(self.baseFilename)) + return logging.handlers.RotatingFileHandler._open(self) + + +class MaxLevelFilter(logging.Filter): + + def __init__(self, level): + self.level = level + + def filter(self, record): + return record.levelno < self.level + + +def setup_logging(verbosity, no_color, user_log_file): + """Configures and sets up all of the logging + """ + + # Determine the level to be logging at. + if verbosity >= 1: + level = "DEBUG" + elif verbosity == -1: + level = "WARNING" + elif verbosity == -2: + level = "ERROR" + elif verbosity <= -3: + level = "CRITICAL" + else: + level = "INFO" + + # The "root" logger should match the "console" level *unless* we also need + # to log to a user log file. + include_user_log = user_log_file is not None + if include_user_log: + additional_log_file = user_log_file + root_level = "DEBUG" + else: + additional_log_file = "/dev/null" + root_level = level + + # Disable any logging besides WARNING unless we have DEBUG level logging + # enabled for vendored libraries. + vendored_log_level = "WARNING" if level in ["INFO", "ERROR"] else "DEBUG" + + # Shorthands for clarity + log_streams = { + "stdout": "ext://sys.stdout", + "stderr": "ext://sys.stderr", + } + handler_classes = { + "stream": "pip._internal.utils.logging.ColorizedStreamHandler", + "file": "pip._internal.utils.logging.BetterRotatingFileHandler", + } + + logging.config.dictConfig({ + "version": 1, + "disable_existing_loggers": False, + "filters": { + "exclude_warnings": { + "()": "pip._internal.utils.logging.MaxLevelFilter", + "level": logging.WARNING, + }, + }, + "formatters": { + "indent": { + "()": IndentingFormatter, + "format": "%(message)s", + }, + }, + "handlers": { + "console": { + "level": level, + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stdout"], + "filters": ["exclude_warnings"], + "formatter": "indent", + }, + "console_errors": { + "level": "WARNING", + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stderr"], + "formatter": "indent", + }, + "user_log": { + "level": "DEBUG", + "class": handler_classes["file"], + "filename": additional_log_file, + "delay": True, + "formatter": "indent", + }, + }, + "root": { + "level": root_level, + "handlers": ["console", "console_errors"] + ( + ["user_log"] if include_user_log else [] + ), + }, + "loggers": { + "pip._vendor": { + "level": vendored_log_level + } + }, + }) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/misc.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/misc.py new file mode 100644 index 0000000..e9e552e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/misc.py @@ -0,0 +1,958 @@ +from __future__ import absolute_import + +import contextlib +import errno +import io +import locale +# we have a submodule named 'logging' which would shadow this if we used the +# regular name: +import logging as std_logging +import os +import posixpath +import re +import shutil +import stat +import subprocess +import sys +import tarfile +import zipfile +from collections import deque + +from pip._vendor import pkg_resources +# NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import. +from pip._vendor.retrying import retry # type: ignore +from pip._vendor.six import PY2 +from pip._vendor.six.moves import input +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.exceptions import CommandError, InstallationError +from pip._internal.locations import ( + running_under_virtualenv, site_packages, user_site, virtualenv_no_global, + write_delete_marker_file, +) +from pip._internal.utils.compat import ( + WINDOWS, console_to_str, expanduser, stdlib_pkgs, +) + +if PY2: + from io import BytesIO as StringIO +else: + from io import StringIO + +__all__ = ['rmtree', 'display_path', 'backup_dir', + 'ask', 'splitext', + 'format_size', 'is_installable_dir', + 'is_svn_page', 'file_contents', + 'split_leading_dir', 'has_leading_dir', + 'normalize_path', + 'renames', 'get_prog', + 'unzip_file', 'untar_file', 'unpack_file', 'call_subprocess', + 'captured_stdout', 'ensure_dir', + 'ARCHIVE_EXTENSIONS', 'SUPPORTED_EXTENSIONS', + 'get_installed_version', 'remove_auth_from_url'] + + +logger = std_logging.getLogger(__name__) + +BZ2_EXTENSIONS = ('.tar.bz2', '.tbz') +XZ_EXTENSIONS = ('.tar.xz', '.txz', '.tlz', '.tar.lz', '.tar.lzma') +ZIP_EXTENSIONS = ('.zip', '.whl') +TAR_EXTENSIONS = ('.tar.gz', '.tgz', '.tar') +ARCHIVE_EXTENSIONS = ( + ZIP_EXTENSIONS + BZ2_EXTENSIONS + TAR_EXTENSIONS + XZ_EXTENSIONS) +SUPPORTED_EXTENSIONS = ZIP_EXTENSIONS + TAR_EXTENSIONS +try: + import bz2 # noqa + SUPPORTED_EXTENSIONS += BZ2_EXTENSIONS +except ImportError: + logger.debug('bz2 module is not available') + +try: + # Only for Python 3.3+ + import lzma # noqa + SUPPORTED_EXTENSIONS += XZ_EXTENSIONS +except ImportError: + logger.debug('lzma module is not available') + + +def import_or_raise(pkg_or_module_string, ExceptionType, *args, **kwargs): + try: + return __import__(pkg_or_module_string) + except ImportError: + raise ExceptionType(*args, **kwargs) + + +def ensure_dir(path): + """os.path.makedirs without EEXIST.""" + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + +def get_prog(): + try: + prog = os.path.basename(sys.argv[0]) + if prog in ('__main__.py', '-c'): + return "%s -m pip" % sys.executable + else: + return prog + except (AttributeError, TypeError, IndexError): + pass + return 'pip' + + +# Retry every half second for up to 3 seconds +@retry(stop_max_delay=3000, wait_fixed=500) +def rmtree(dir, ignore_errors=False): + shutil.rmtree(dir, ignore_errors=ignore_errors, + onerror=rmtree_errorhandler) + + +def rmtree_errorhandler(func, path, exc_info): + """On Windows, the files in .svn are read-only, so when rmtree() tries to + remove them, an exception is thrown. We catch that here, remove the + read-only attribute, and hopefully continue without problems.""" + # if file type currently read only + if os.stat(path).st_mode & stat.S_IREAD: + # convert to read/write + os.chmod(path, stat.S_IWRITE) + # use the original function to repeat the operation + func(path) + return + else: + raise + + +def display_path(path): + """Gives the display value for a given path, making it relative to cwd + if possible.""" + path = os.path.normcase(os.path.abspath(path)) + if sys.version_info[0] == 2: + path = path.decode(sys.getfilesystemencoding(), 'replace') + path = path.encode(sys.getdefaultencoding(), 'replace') + if path.startswith(os.getcwd() + os.path.sep): + path = '.' + path[len(os.getcwd()):] + return path + + +def backup_dir(dir, ext='.bak'): + """Figure out the name of a directory to back up the given dir to + (adding .bak, .bak2, etc)""" + n = 1 + extension = ext + while os.path.exists(dir + extension): + n += 1 + extension = ext + str(n) + return dir + extension + + +def ask_path_exists(message, options): + for action in os.environ.get('PIP_EXISTS_ACTION', '').split(): + if action in options: + return action + return ask(message, options) + + +def ask(message, options): + """Ask the message interactively, with the given possible responses""" + while 1: + if os.environ.get('PIP_NO_INPUT'): + raise Exception( + 'No input was expected ($PIP_NO_INPUT set); question: %s' % + message + ) + response = input(message) + response = response.strip().lower() + if response not in options: + print( + 'Your response (%r) was not one of the expected responses: ' + '%s' % (response, ', '.join(options)) + ) + else: + return response + + +def format_size(bytes): + if bytes > 1000 * 1000: + return '%.1fMB' % (bytes / 1000.0 / 1000) + elif bytes > 10 * 1000: + return '%ikB' % (bytes / 1000) + elif bytes > 1000: + return '%.1fkB' % (bytes / 1000.0) + else: + return '%ibytes' % bytes + + +def is_installable_dir(path): + """Is path is a directory containing setup.py or pyproject.toml? + """ + if not os.path.isdir(path): + return False + setup_py = os.path.join(path, 'setup.py') + if os.path.isfile(setup_py): + return True + pyproject_toml = os.path.join(path, 'pyproject.toml') + if os.path.isfile(pyproject_toml): + return True + return False + + +def is_svn_page(html): + """ + Returns true if the page appears to be the index page of an svn repository + """ + return (re.search(r'[^<]*Revision \d+:', html) and + re.search(r'Powered by (?:<a[^>]*?>)?Subversion', html, re.I)) + + +def file_contents(filename): + with open(filename, 'rb') as fp: + return fp.read().decode('utf-8') + + +def read_chunks(file, size=io.DEFAULT_BUFFER_SIZE): + """Yield pieces of data from a file-like object until EOF.""" + while True: + chunk = file.read(size) + if not chunk: + break + yield chunk + + +def split_leading_dir(path): + path = path.lstrip('/').lstrip('\\') + if '/' in path and (('\\' in path and path.find('/') < path.find('\\')) or + '\\' not in path): + return path.split('/', 1) + elif '\\' in path: + return path.split('\\', 1) + else: + return path, '' + + +def has_leading_dir(paths): + """Returns true if all the paths have the same leading path name + (i.e., everything is in one subdirectory in an archive)""" + common_prefix = None + for path in paths: + prefix, rest = split_leading_dir(path) + if not prefix: + return False + elif common_prefix is None: + common_prefix = prefix + elif prefix != common_prefix: + return False + return True + + +def normalize_path(path, resolve_symlinks=True): + """ + Convert a path to its canonical, case-normalized, absolute version. + + """ + path = expanduser(path) + if resolve_symlinks: + path = os.path.realpath(path) + else: + path = os.path.abspath(path) + return os.path.normcase(path) + + +def splitext(path): + """Like os.path.splitext, but take off .tar too""" + base, ext = posixpath.splitext(path) + if base.lower().endswith('.tar'): + ext = base[-4:] + ext + base = base[:-4] + return base, ext + + +def renames(old, new): + """Like os.renames(), but handles renaming across devices.""" + # Implementation borrowed from os.renames(). + head, tail = os.path.split(new) + if head and tail and not os.path.exists(head): + os.makedirs(head) + + shutil.move(old, new) + + head, tail = os.path.split(old) + if head and tail: + try: + os.removedirs(head) + except OSError: + pass + + +def is_local(path): + """ + Return True if this is a path pip is allowed to modify. + + If we're in a virtualenv, sys.prefix points to the virtualenv's + prefix; only sys.prefix is considered local. + + If we're not in a virtualenv, in general we can modify anything. + However, if the OS vendor has configured distutils to install + somewhere other than sys.prefix (which could be a subdirectory of + sys.prefix, e.g. /usr/local), we consider sys.prefix itself nonlocal + and the domain of the OS vendor. (In other words, everything _other + than_ sys.prefix is considered local.) + + """ + + path = normalize_path(path) + prefix = normalize_path(sys.prefix) + + if running_under_virtualenv(): + return path.startswith(normalize_path(sys.prefix)) + else: + from pip._internal.locations import distutils_scheme + if path.startswith(prefix): + for local_path in distutils_scheme("").values(): + if path.startswith(normalize_path(local_path)): + return True + return False + else: + return True + + +def dist_is_local(dist): + """ + Return True if given Distribution object is installed somewhere pip + is allowed to modify. + + """ + return is_local(dist_location(dist)) + + +def dist_in_usersite(dist): + """ + Return True if given Distribution is installed in user site. + """ + norm_path = normalize_path(dist_location(dist)) + return norm_path.startswith(normalize_path(user_site)) + + +def dist_in_site_packages(dist): + """ + Return True if given Distribution is installed in + sysconfig.get_python_lib(). + """ + return normalize_path( + dist_location(dist) + ).startswith(normalize_path(site_packages)) + + +def dist_is_editable(dist): + """Is distribution an editable install?""" + for path_item in sys.path: + egg_link = os.path.join(path_item, dist.project_name + '.egg-link') + if os.path.isfile(egg_link): + return True + return False + + +def get_installed_distributions(local_only=True, + skip=stdlib_pkgs, + include_editables=True, + editables_only=False, + user_only=False): + """ + Return a list of installed Distribution objects. + + If ``local_only`` is True (default), only return installations + local to the current virtualenv, if in a virtualenv. + + ``skip`` argument is an iterable of lower-case project names to + ignore; defaults to stdlib_pkgs + + If ``include_editables`` is False, don't report editables. + + If ``editables_only`` is True , only report editables. + + If ``user_only`` is True , only report installations in the user + site directory. + + """ + if local_only: + local_test = dist_is_local + else: + def local_test(d): + return True + + if include_editables: + def editable_test(d): + return True + else: + def editable_test(d): + return not dist_is_editable(d) + + if editables_only: + def editables_only_test(d): + return dist_is_editable(d) + else: + def editables_only_test(d): + return True + + if user_only: + user_test = dist_in_usersite + else: + def user_test(d): + return True + + return [d for d in pkg_resources.working_set + if local_test(d) and + d.key not in skip and + editable_test(d) and + editables_only_test(d) and + user_test(d) + ] + + +def egg_link_path(dist): + """ + Return the path for the .egg-link file if it exists, otherwise, None. + + There's 3 scenarios: + 1) not in a virtualenv + try to find in site.USER_SITE, then site_packages + 2) in a no-global virtualenv + try to find in site_packages + 3) in a yes-global virtualenv + try to find in site_packages, then site.USER_SITE + (don't look in global location) + + For #1 and #3, there could be odd cases, where there's an egg-link in 2 + locations. + + This method will just return the first one found. + """ + sites = [] + if running_under_virtualenv(): + if virtualenv_no_global(): + sites.append(site_packages) + else: + sites.append(site_packages) + if user_site: + sites.append(user_site) + else: + if user_site: + sites.append(user_site) + sites.append(site_packages) + + for site in sites: + egglink = os.path.join(site, dist.project_name) + '.egg-link' + if os.path.isfile(egglink): + return egglink + + +def dist_location(dist): + """ + Get the site-packages location of this distribution. Generally + this is dist.location, except in the case of develop-installed + packages, where dist.location is the source code location, and we + want to know where the egg-link file is. + + """ + egg_link = egg_link_path(dist) + if egg_link: + return egg_link + return dist.location + + +def current_umask(): + """Get the current umask which involves having to set it temporarily.""" + mask = os.umask(0) + os.umask(mask) + return mask + + +def unzip_file(filename, location, flatten=True): + """ + Unzip the file (with path `filename`) to the destination `location`. All + files are written based on system defaults and umask (i.e. permissions are + not preserved), except that regular file members with any execute + permissions (user, group, or world) have "chmod +x" applied after being + written. Note that for windows, any execute changes using os.chmod are + no-ops per the python docs. + """ + ensure_dir(location) + zipfp = open(filename, 'rb') + try: + zip = zipfile.ZipFile(zipfp, allowZip64=True) + leading = has_leading_dir(zip.namelist()) and flatten + for info in zip.infolist(): + name = info.filename + data = zip.read(name) + fn = name + if leading: + fn = split_leading_dir(name)[1] + fn = os.path.join(location, fn) + dir = os.path.dirname(fn) + if fn.endswith('/') or fn.endswith('\\'): + # A directory + ensure_dir(fn) + else: + ensure_dir(dir) + fp = open(fn, 'wb') + try: + fp.write(data) + finally: + fp.close() + mode = info.external_attr >> 16 + # if mode and regular file and any execute permissions for + # user/group/world? + if mode and stat.S_ISREG(mode) and mode & 0o111: + # make dest file have execute for user/group/world + # (chmod +x) no-op on windows per python docs + os.chmod(fn, (0o777 - current_umask() | 0o111)) + finally: + zipfp.close() + + +def untar_file(filename, location): + """ + Untar the file (with path `filename`) to the destination `location`. + All files are written based on system defaults and umask (i.e. permissions + are not preserved), except that regular file members with any execute + permissions (user, group, or world) have "chmod +x" applied after being + written. Note that for windows, any execute changes using os.chmod are + no-ops per the python docs. + """ + ensure_dir(location) + if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'): + mode = 'r:gz' + elif filename.lower().endswith(BZ2_EXTENSIONS): + mode = 'r:bz2' + elif filename.lower().endswith(XZ_EXTENSIONS): + mode = 'r:xz' + elif filename.lower().endswith('.tar'): + mode = 'r' + else: + logger.warning( + 'Cannot determine compression type for file %s', filename, + ) + mode = 'r:*' + tar = tarfile.open(filename, mode) + try: + # note: python<=2.5 doesn't seem to know about pax headers, filter them + leading = has_leading_dir([ + member.name for member in tar.getmembers() + if member.name != 'pax_global_header' + ]) + for member in tar.getmembers(): + fn = member.name + if fn == 'pax_global_header': + continue + if leading: + fn = split_leading_dir(fn)[1] + path = os.path.join(location, fn) + if member.isdir(): + ensure_dir(path) + elif member.issym(): + try: + tar._extract_member(member, path) + except Exception as exc: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + logger.warning( + 'In the tar file %s the member %s is invalid: %s', + filename, member.name, exc, + ) + continue + else: + try: + fp = tar.extractfile(member) + except (KeyError, AttributeError) as exc: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + logger.warning( + 'In the tar file %s the member %s is invalid: %s', + filename, member.name, exc, + ) + continue + ensure_dir(os.path.dirname(path)) + with open(path, 'wb') as destfp: + shutil.copyfileobj(fp, destfp) + fp.close() + # Update the timestamp (useful for cython compiled files) + tar.utime(member, path) + # member have any execute permissions for user/group/world? + if member.mode & 0o111: + # make dest file have execute for user/group/world + # no-op on windows per python docs + os.chmod(path, (0o777 - current_umask() | 0o111)) + finally: + tar.close() + + +def unpack_file(filename, location, content_type, link): + filename = os.path.realpath(filename) + if (content_type == 'application/zip' or + filename.lower().endswith(ZIP_EXTENSIONS) or + zipfile.is_zipfile(filename)): + unzip_file( + filename, + location, + flatten=not filename.endswith('.whl') + ) + elif (content_type == 'application/x-gzip' or + tarfile.is_tarfile(filename) or + filename.lower().endswith( + TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS)): + untar_file(filename, location) + elif (content_type and content_type.startswith('text/html') and + is_svn_page(file_contents(filename))): + # We don't really care about this + from pip._internal.vcs.subversion import Subversion + Subversion('svn+' + link.url).unpack(location) + else: + # FIXME: handle? + # FIXME: magic signatures? + logger.critical( + 'Cannot unpack file %s (downloaded from %s, content-type: %s); ' + 'cannot detect archive format', + filename, location, content_type, + ) + raise InstallationError( + 'Cannot determine archive format of %s' % location + ) + + +def call_subprocess(cmd, show_stdout=True, cwd=None, + on_returncode='raise', + command_desc=None, + extra_environ=None, unset_environ=None, spinner=None): + """ + Args: + unset_environ: an iterable of environment variable names to unset + prior to calling subprocess.Popen(). + """ + if unset_environ is None: + unset_environ = [] + # This function's handling of subprocess output is confusing and I + # previously broke it terribly, so as penance I will write a long comment + # explaining things. + # + # The obvious thing that affects output is the show_stdout= + # kwarg. show_stdout=True means, let the subprocess write directly to our + # stdout. Even though it is nominally the default, it is almost never used + # inside pip (and should not be used in new code without a very good + # reason); as of 2016-02-22 it is only used in a few places inside the VCS + # wrapper code. Ideally we should get rid of it entirely, because it + # creates a lot of complexity here for a rarely used feature. + # + # Most places in pip set show_stdout=False. What this means is: + # - We connect the child stdout to a pipe, which we read. + # - By default, we hide the output but show a spinner -- unless the + # subprocess exits with an error, in which case we show the output. + # - If the --verbose option was passed (= loglevel is DEBUG), then we show + # the output unconditionally. (But in this case we don't want to show + # the output a second time if it turns out that there was an error.) + # + # stderr is always merged with stdout (even if show_stdout=True). + if show_stdout: + stdout = None + else: + stdout = subprocess.PIPE + if command_desc is None: + cmd_parts = [] + for part in cmd: + if ' ' in part or '\n' in part or '"' in part or "'" in part: + part = '"%s"' % part.replace('"', '\\"') + cmd_parts.append(part) + command_desc = ' '.join(cmd_parts) + logger.debug("Running command %s", command_desc) + env = os.environ.copy() + if extra_environ: + env.update(extra_environ) + for name in unset_environ: + env.pop(name, None) + try: + proc = subprocess.Popen( + cmd, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, + stdout=stdout, cwd=cwd, env=env, + ) + proc.stdin.close() + except Exception as exc: + logger.critical( + "Error %s while executing command %s", exc, command_desc, + ) + raise + all_output = [] + if stdout is not None: + while True: + line = console_to_str(proc.stdout.readline()) + if not line: + break + line = line.rstrip() + all_output.append(line + '\n') + if logger.getEffectiveLevel() <= std_logging.DEBUG: + # Show the line immediately + logger.debug(line) + else: + # Update the spinner + if spinner is not None: + spinner.spin() + try: + proc.wait() + finally: + if proc.stdout: + proc.stdout.close() + if spinner is not None: + if proc.returncode: + spinner.finish("error") + else: + spinner.finish("done") + if proc.returncode: + if on_returncode == 'raise': + if (logger.getEffectiveLevel() > std_logging.DEBUG and + not show_stdout): + logger.info( + 'Complete output from command %s:', command_desc, + ) + logger.info( + ''.join(all_output) + + '\n----------------------------------------' + ) + raise InstallationError( + 'Command "%s" failed with error code %s in %s' + % (command_desc, proc.returncode, cwd)) + elif on_returncode == 'warn': + logger.warning( + 'Command "%s" had error code %s in %s', + command_desc, proc.returncode, cwd, + ) + elif on_returncode == 'ignore': + pass + else: + raise ValueError('Invalid value: on_returncode=%s' % + repr(on_returncode)) + if not show_stdout: + return ''.join(all_output) + + +def read_text_file(filename): + """Return the contents of *filename*. + + Try to decode the file contents with utf-8, the preferred system encoding + (e.g., cp1252 on some Windows machines), and latin1, in that order. + Decoding a byte string with latin1 will never raise an error. In the worst + case, the returned string will contain some garbage characters. + + """ + with open(filename, 'rb') as fp: + data = fp.read() + + encodings = ['utf-8', locale.getpreferredencoding(False), 'latin1'] + for enc in encodings: + try: + data = data.decode(enc) + except UnicodeDecodeError: + continue + break + + assert type(data) != bytes # Latin1 should have worked. + return data + + +def _make_build_dir(build_dir): + os.makedirs(build_dir) + write_delete_marker_file(build_dir) + + +class FakeFile(object): + """Wrap a list of lines in an object with readline() to make + ConfigParser happy.""" + def __init__(self, lines): + self._gen = (l for l in lines) + + def readline(self): + try: + try: + return next(self._gen) + except NameError: + return self._gen.next() + except StopIteration: + return '' + + def __iter__(self): + return self._gen + + +class StreamWrapper(StringIO): + + @classmethod + def from_stream(cls, orig_stream): + cls.orig_stream = orig_stream + return cls() + + # compileall.compile_dir() needs stdout.encoding to print to stdout + @property + def encoding(self): + return self.orig_stream.encoding + + +@contextlib.contextmanager +def captured_output(stream_name): + """Return a context manager used by captured_stdout/stdin/stderr + that temporarily replaces the sys stream *stream_name* with a StringIO. + + Taken from Lib/support/__init__.py in the CPython repo. + """ + orig_stdout = getattr(sys, stream_name) + setattr(sys, stream_name, StreamWrapper.from_stream(orig_stdout)) + try: + yield getattr(sys, stream_name) + finally: + setattr(sys, stream_name, orig_stdout) + + +def captured_stdout(): + """Capture the output of sys.stdout: + + with captured_stdout() as stdout: + print('hello') + self.assertEqual(stdout.getvalue(), 'hello\n') + + Taken from Lib/support/__init__.py in the CPython repo. + """ + return captured_output('stdout') + + +class cached_property(object): + """A property that is only computed once per instance and then replaces + itself with an ordinary attribute. Deleting the attribute resets the + property. + + Source: https://github.com/bottlepy/bottle/blob/0.11.5/bottle.py#L175 + """ + + def __init__(self, func): + self.__doc__ = getattr(func, '__doc__') + self.func = func + + def __get__(self, obj, cls): + if obj is None: + # We're being accessed from the class itself, not from an object + return self + value = obj.__dict__[self.func.__name__] = self.func(obj) + return value + + +def get_installed_version(dist_name, working_set=None): + """Get the installed version of dist_name avoiding pkg_resources cache""" + # Create a requirement that we'll look for inside of setuptools. + req = pkg_resources.Requirement.parse(dist_name) + + if working_set is None: + # We want to avoid having this cached, so we need to construct a new + # working set each time. + working_set = pkg_resources.WorkingSet() + + # Get the installed distribution from our working set + dist = working_set.find(req) + + # Check to see if we got an installed distribution or not, if we did + # we want to return it's version. + return dist.version if dist else None + + +def consume(iterator): + """Consume an iterable at C speed.""" + deque(iterator, maxlen=0) + + +# Simulates an enum +def enum(*sequential, **named): + enums = dict(zip(sequential, range(len(sequential))), **named) + reverse = {value: key for key, value in enums.items()} + enums['reverse_mapping'] = reverse + return type('Enum', (), enums) + + +def make_vcs_requirement_url(repo_url, rev, egg_project_name, subdir=None): + """ + Return the URL for a VCS requirement. + + Args: + repo_url: the remote VCS url, with any needed VCS prefix (e.g. "git+"). + """ + req = '{}@{}#egg={}'.format(repo_url, rev, egg_project_name) + if subdir: + req += '&subdirectory={}'.format(subdir) + + return req + + +def split_auth_from_netloc(netloc): + """ + Parse out and remove the auth information from a netloc. + + Returns: (netloc, (username, password)). + """ + if '@' not in netloc: + return netloc, (None, None) + + # Split from the right because that's how urllib.parse.urlsplit() + # behaves if more than one @ is present (which can be checked using + # the password attribute of urlsplit()'s return value). + auth, netloc = netloc.rsplit('@', 1) + if ':' in auth: + # Split from the left because that's how urllib.parse.urlsplit() + # behaves if more than one : is present (which again can be checked + # using the password attribute of the return value) + user_pass = tuple(auth.split(':', 1)) + else: + user_pass = auth, None + + return netloc, user_pass + + +def remove_auth_from_url(url): + # Return a copy of url with 'username:password@' removed. + # username/pass params are passed to subversion through flags + # and are not recognized in the url. + + # parsed url + purl = urllib_parse.urlsplit(url) + netloc, user_pass = split_auth_from_netloc(purl.netloc) + + # stripped url + url_pieces = ( + purl.scheme, netloc, purl.path, purl.query, purl.fragment + ) + surl = urllib_parse.urlunsplit(url_pieces) + return surl + + +def protect_pip_from_modification_on_windows(modifying_pip): + """Protection of pip.exe from modification on Windows + + On Windows, any operation modifying pip should be run as: + python -m pip ... + """ + pip_names = [ + "pip.exe", + "pip{}.exe".format(sys.version_info[0]), + "pip{}.{}.exe".format(*sys.version_info[:2]) + ] + + # See https://github.com/pypa/pip/issues/1299 for more discussion + should_show_use_python_msg = ( + modifying_pip and + WINDOWS and + os.path.basename(sys.argv[0]) in pip_names + ) + + if should_show_use_python_msg: + new_command = [ + sys.executable, "-m", "pip" + ] + sys.argv[1:] + raise CommandError( + 'To modify pip, please run the following command:\n{}' + .format(" ".join(new_command)) + ) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/models.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/models.py new file mode 100644 index 0000000..d5cb80a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/models.py @@ -0,0 +1,40 @@ +"""Utilities for defining models +""" + +import operator + + +class KeyBasedCompareMixin(object): + """Provides comparision capabilities that is based on a key + """ + + def __init__(self, key, defining_class): + self._compare_key = key + self._defining_class = defining_class + + def __hash__(self): + return hash(self._compare_key) + + def __lt__(self, other): + return self._compare(other, operator.__lt__) + + def __le__(self, other): + return self._compare(other, operator.__le__) + + def __gt__(self, other): + return self._compare(other, operator.__gt__) + + def __ge__(self, other): + return self._compare(other, operator.__ge__) + + def __eq__(self, other): + return self._compare(other, operator.__eq__) + + def __ne__(self, other): + return self._compare(other, operator.__ne__) + + def _compare(self, other, method): + if not isinstance(other, self._defining_class): + return NotImplemented + + return method(self._compare_key, other._compare_key) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/outdated.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/outdated.py new file mode 100644 index 0000000..5bfbfe1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/outdated.py @@ -0,0 +1,154 @@ +from __future__ import absolute_import + +import datetime +import json +import logging +import os.path +import sys + +from pip._vendor import lockfile, pkg_resources +from pip._vendor.packaging import version as packaging_version + +from pip._internal.index import PackageFinder +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.filesystem import check_path_owner +from pip._internal.utils.misc import ensure_dir, get_installed_version + +SELFCHECK_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ" + + +logger = logging.getLogger(__name__) + + +class SelfCheckState(object): + def __init__(self, cache_dir): + self.state = {} + self.statefile_path = None + + # Try to load the existing state + if cache_dir: + self.statefile_path = os.path.join(cache_dir, "selfcheck.json") + try: + with open(self.statefile_path) as statefile: + self.state = json.load(statefile)[sys.prefix] + except (IOError, ValueError, KeyError): + # Explicitly suppressing exceptions, since we don't want to + # error out if the cache file is invalid. + pass + + def save(self, pypi_version, current_time): + # If we do not have a path to cache in, don't bother saving. + if not self.statefile_path: + return + + # Check to make sure that we own the directory + if not check_path_owner(os.path.dirname(self.statefile_path)): + return + + # Now that we've ensured the directory is owned by this user, we'll go + # ahead and make sure that all our directories are created. + ensure_dir(os.path.dirname(self.statefile_path)) + + # Attempt to write out our version check file + with lockfile.LockFile(self.statefile_path): + if os.path.exists(self.statefile_path): + with open(self.statefile_path) as statefile: + state = json.load(statefile) + else: + state = {} + + state[sys.prefix] = { + "last_check": current_time.strftime(SELFCHECK_DATE_FMT), + "pypi_version": pypi_version, + } + + with open(self.statefile_path, "w") as statefile: + json.dump(state, statefile, sort_keys=True, + separators=(",", ":")) + + +def was_installed_by_pip(pkg): + """Checks whether pkg was installed by pip + + This is used not to display the upgrade message when pip is in fact + installed by system package manager, such as dnf on Fedora. + """ + try: + dist = pkg_resources.get_distribution(pkg) + return (dist.has_metadata('INSTALLER') and + 'pip' in dist.get_metadata_lines('INSTALLER')) + except pkg_resources.DistributionNotFound: + return False + + +def pip_version_check(session, options): + """Check for an update for pip. + + Limit the frequency of checks to once per week. State is stored either in + the active virtualenv or in the user's USER_CACHE_DIR keyed off the prefix + of the pip script path. + """ + installed_version = get_installed_version("pip") + if not installed_version: + return + + pip_version = packaging_version.parse(installed_version) + pypi_version = None + + try: + state = SelfCheckState(cache_dir=options.cache_dir) + + current_time = datetime.datetime.utcnow() + # Determine if we need to refresh the state + if "last_check" in state.state and "pypi_version" in state.state: + last_check = datetime.datetime.strptime( + state.state["last_check"], + SELFCHECK_DATE_FMT + ) + if (current_time - last_check).total_seconds() < 7 * 24 * 60 * 60: + pypi_version = state.state["pypi_version"] + + # Refresh the version if we need to or just see if we need to warn + if pypi_version is None: + # Lets use PackageFinder to see what the latest pip version is + finder = PackageFinder( + find_links=options.find_links, + index_urls=[options.index_url] + options.extra_index_urls, + allow_all_prereleases=False, # Explicitly set to False + trusted_hosts=options.trusted_hosts, + process_dependency_links=options.process_dependency_links, + session=session, + ) + all_candidates = finder.find_all_candidates("pip") + if not all_candidates: + return + pypi_version = str( + max(all_candidates, key=lambda c: c.version).version + ) + + # save that we've performed a check + state.save(pypi_version, current_time) + + remote_version = packaging_version.parse(pypi_version) + + # Determine if our pypi_version is older + if (pip_version < remote_version and + pip_version.base_version != remote_version.base_version and + was_installed_by_pip('pip')): + # Advise "python -m pip" on Windows to avoid issues + # with overwriting pip.exe. + if WINDOWS: + pip_cmd = "python -m pip" + else: + pip_cmd = "pip" + logger.warning( + "You are using pip version %s, however version %s is " + "available.\nYou should consider upgrading via the " + "'%s install --upgrade pip' command.", + pip_version, pypi_version, pip_cmd + ) + except Exception: + logger.debug( + "There was an error checking the latest version of pip", + exc_info=True, + ) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/packaging.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/packaging.py new file mode 100644 index 0000000..c43142f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/packaging.py @@ -0,0 +1,75 @@ +from __future__ import absolute_import + +import logging +import sys +from email.parser import FeedParser # type: ignore + +from pip._vendor import pkg_resources +from pip._vendor.packaging import specifiers, version + +from pip._internal import exceptions +from pip._internal.utils.misc import display_path + +logger = logging.getLogger(__name__) + + +def check_requires_python(requires_python): + """ + Check if the python version in use match the `requires_python` specifier. + + Returns `True` if the version of python in use matches the requirement. + Returns `False` if the version of python in use does not matches the + requirement. + + Raises an InvalidSpecifier if `requires_python` have an invalid format. + """ + if requires_python is None: + # The package provides no information + return True + requires_python_specifier = specifiers.SpecifierSet(requires_python) + + # We only use major.minor.micro + python_version = version.parse('.'.join(map(str, sys.version_info[:3]))) + return python_version in requires_python_specifier + + +def get_metadata(dist): + if (isinstance(dist, pkg_resources.DistInfoDistribution) and + dist.has_metadata('METADATA')): + metadata = dist.get_metadata('METADATA') + elif dist.has_metadata('PKG-INFO'): + metadata = dist.get_metadata('PKG-INFO') + else: + logger.warning("No metadata found in %s", display_path(dist.location)) + metadata = '' + + feed_parser = FeedParser() + feed_parser.feed(metadata) + return feed_parser.close() + + +def check_dist_requires_python(dist): + pkg_info_dict = get_metadata(dist) + requires_python = pkg_info_dict.get('Requires-Python') + try: + if not check_requires_python(requires_python): + raise exceptions.UnsupportedPythonVersion( + "%s requires Python '%s' but the running Python is %s" % ( + dist.project_name, + requires_python, + '.'.join(map(str, sys.version_info[:3])),) + ) + except specifiers.InvalidSpecifier as e: + logger.warning( + "Package %s has an invalid Requires-Python entry %s - %s", + dist.project_name, requires_python, e, + ) + return + + +def get_installer(dist): + if dist.has_metadata('INSTALLER'): + for line in dist.get_metadata_lines('INSTALLER'): + if line.strip(): + return line.strip() + return '' diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/setuptools_build.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/setuptools_build.py new file mode 100644 index 0000000..03973e9 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/setuptools_build.py @@ -0,0 +1,8 @@ +# Shim to wrap setup.py invocation with setuptools +SETUPTOOLS_SHIM = ( + "import setuptools, tokenize;__file__=%r;" + "f=getattr(tokenize, 'open', open)(__file__);" + "code=f.read().replace('\\r\\n', '\\n');" + "f.close();" + "exec(compile(code, __file__, 'exec'))" +) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/temp_dir.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/temp_dir.py new file mode 100644 index 0000000..edc506b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/temp_dir.py @@ -0,0 +1,82 @@ +from __future__ import absolute_import + +import logging +import os.path +import tempfile + +from pip._internal.utils.misc import rmtree + +logger = logging.getLogger(__name__) + + +class TempDirectory(object): + """Helper class that owns and cleans up a temporary directory. + + This class can be used as a context manager or as an OO representation of a + temporary directory. + + Attributes: + path + Location to the created temporary directory or None + delete + Whether the directory should be deleted when exiting + (when used as a contextmanager) + + Methods: + create() + Creates a temporary directory and stores its path in the path + attribute. + cleanup() + Deletes the temporary directory and sets path attribute to None + + When used as a context manager, a temporary directory is created on + entering the context and, if the delete attribute is True, on exiting the + context the created directory is deleted. + """ + + def __init__(self, path=None, delete=None, kind="temp"): + super(TempDirectory, self).__init__() + + if path is None and delete is None: + # If we were not given an explicit directory, and we were not given + # an explicit delete option, then we'll default to deleting. + delete = True + + self.path = path + self.delete = delete + self.kind = kind + + def __repr__(self): + return "<{} {!r}>".format(self.__class__.__name__, self.path) + + def __enter__(self): + self.create() + return self + + def __exit__(self, exc, value, tb): + if self.delete: + self.cleanup() + + def create(self): + """Create a temporary directory and store it's path in self.path + """ + if self.path is not None: + logger.debug( + "Skipped creation of temporary directory: {}".format(self.path) + ) + return + # We realpath here because some systems have their default tmpdir + # symlinked to another directory. This tends to confuse build + # scripts, so we canonicalize the path by traversing potential + # symlinks here. + self.path = os.path.realpath( + tempfile.mkdtemp(prefix="pip-{}-".format(self.kind)) + ) + logger.debug("Created temporary directory: {}".format(self.path)) + + def cleanup(self): + """Remove the temporary directory created and reset state + """ + if self.path is not None and os.path.exists(self.path): + rmtree(self.path) + self.path = None diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/typing.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/typing.py new file mode 100644 index 0000000..e085cdf --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/typing.py @@ -0,0 +1,29 @@ +"""For neatly implementing static typing in pip. + +`mypy` - the static type analysis tool we use - uses the `typing` module, which +provides core functionality fundamental to mypy's functioning. + +Generally, `typing` would be imported at runtime and used in that fashion - +it acts as a no-op at runtime and does not have any run-time overhead by +design. + +As it turns out, `typing` is not vendorable - it uses separate sources for +Python 2/Python 3. Thus, this codebase can not expect it to be present. +To work around this, mypy allows the typing import to be behind a False-y +optional to prevent it from running at runtime and type-comments can be used +to remove the need for the types to be accessible directly during runtime. + +This module provides the False-y guard in a nicely named fashion so that a +curious maintainer can reach here to read this. + +In pip, all static-typing related imports should be guarded as follows: + + from pip._internal.utils.typing import MYPY_CHECK_RUNNING + + if MYPY_CHECK_RUNNING: + from typing import ... # noqa: F401 + +Ref: https://github.com/python/mypy/issues/3216 +""" + +MYPY_CHECK_RUNNING = False diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/ui.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/ui.py new file mode 100644 index 0000000..6bab904 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/utils/ui.py @@ -0,0 +1,421 @@ +from __future__ import absolute_import, division + +import contextlib +import itertools +import logging +import sys +import time +from signal import SIGINT, default_int_handler, signal + +from pip._vendor import six +from pip._vendor.progress.bar import ( + Bar, ChargingBar, FillingCirclesBar, FillingSquaresBar, IncrementalBar, + ShadyBar, +) +from pip._vendor.progress.helpers import HIDE_CURSOR, SHOW_CURSOR, WritelnMixin +from pip._vendor.progress.spinner import Spinner + +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.logging import get_indentation +from pip._internal.utils.misc import format_size +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any # noqa: F401 + +try: + from pip._vendor import colorama +# Lots of different errors can come from this, including SystemError and +# ImportError. +except Exception: + colorama = None + +logger = logging.getLogger(__name__) + + +def _select_progress_class(preferred, fallback): + encoding = getattr(preferred.file, "encoding", None) + + # If we don't know what encoding this file is in, then we'll just assume + # that it doesn't support unicode and use the ASCII bar. + if not encoding: + return fallback + + # Collect all of the possible characters we want to use with the preferred + # bar. + characters = [ + getattr(preferred, "empty_fill", six.text_type()), + getattr(preferred, "fill", six.text_type()), + ] + characters += list(getattr(preferred, "phases", [])) + + # Try to decode the characters we're using for the bar using the encoding + # of the given file, if this works then we'll assume that we can use the + # fancier bar and if not we'll fall back to the plaintext bar. + try: + six.text_type().join(characters).encode(encoding) + except UnicodeEncodeError: + return fallback + else: + return preferred + + +_BaseBar = _select_progress_class(IncrementalBar, Bar) # type: Any + + +class InterruptibleMixin(object): + """ + Helper to ensure that self.finish() gets called on keyboard interrupt. + + This allows downloads to be interrupted without leaving temporary state + (like hidden cursors) behind. + + This class is similar to the progress library's existing SigIntMixin + helper, but as of version 1.2, that helper has the following problems: + + 1. It calls sys.exit(). + 2. It discards the existing SIGINT handler completely. + 3. It leaves its own handler in place even after an uninterrupted finish, + which will have unexpected delayed effects if the user triggers an + unrelated keyboard interrupt some time after a progress-displaying + download has already completed, for example. + """ + + def __init__(self, *args, **kwargs): + """ + Save the original SIGINT handler for later. + """ + super(InterruptibleMixin, self).__init__(*args, **kwargs) + + self.original_handler = signal(SIGINT, self.handle_sigint) + + # If signal() returns None, the previous handler was not installed from + # Python, and we cannot restore it. This probably should not happen, + # but if it does, we must restore something sensible instead, at least. + # The least bad option should be Python's default SIGINT handler, which + # just raises KeyboardInterrupt. + if self.original_handler is None: + self.original_handler = default_int_handler + + def finish(self): + """ + Restore the original SIGINT handler after finishing. + + This should happen regardless of whether the progress display finishes + normally, or gets interrupted. + """ + super(InterruptibleMixin, self).finish() + signal(SIGINT, self.original_handler) + + def handle_sigint(self, signum, frame): + """ + Call self.finish() before delegating to the original SIGINT handler. + + This handler should only be in place while the progress display is + active. + """ + self.finish() + self.original_handler(signum, frame) + + +class SilentBar(Bar): + + def update(self): + pass + + +class BlueEmojiBar(IncrementalBar): + + suffix = "%(percent)d%%" + bar_prefix = " " + bar_suffix = " " + phases = (u"\U0001F539", u"\U0001F537", u"\U0001F535") # type: Any + + +class DownloadProgressMixin(object): + + def __init__(self, *args, **kwargs): + super(DownloadProgressMixin, self).__init__(*args, **kwargs) + self.message = (" " * (get_indentation() + 2)) + self.message + + @property + def downloaded(self): + return format_size(self.index) + + @property + def download_speed(self): + # Avoid zero division errors... + if self.avg == 0.0: + return "..." + return format_size(1 / self.avg) + "/s" + + @property + def pretty_eta(self): + if self.eta: + return "eta %s" % self.eta_td + return "" + + def iter(self, it, n=1): + for x in it: + yield x + self.next(n) + self.finish() + + +class WindowsMixin(object): + + def __init__(self, *args, **kwargs): + # The Windows terminal does not support the hide/show cursor ANSI codes + # even with colorama. So we'll ensure that hide_cursor is False on + # Windows. + # This call neds to go before the super() call, so that hide_cursor + # is set in time. The base progress bar class writes the "hide cursor" + # code to the terminal in its init, so if we don't set this soon + # enough, we get a "hide" with no corresponding "show"... + if WINDOWS and self.hide_cursor: + self.hide_cursor = False + + super(WindowsMixin, self).__init__(*args, **kwargs) + + # Check if we are running on Windows and we have the colorama module, + # if we do then wrap our file with it. + if WINDOWS and colorama: + self.file = colorama.AnsiToWin32(self.file) + # The progress code expects to be able to call self.file.isatty() + # but the colorama.AnsiToWin32() object doesn't have that, so we'll + # add it. + self.file.isatty = lambda: self.file.wrapped.isatty() + # The progress code expects to be able to call self.file.flush() + # but the colorama.AnsiToWin32() object doesn't have that, so we'll + # add it. + self.file.flush = lambda: self.file.wrapped.flush() + + +class BaseDownloadProgressBar(WindowsMixin, InterruptibleMixin, + DownloadProgressMixin): + + file = sys.stdout + message = "%(percent)d%%" + suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s" + +# NOTE: The "type: ignore" comments on the following classes are there to +# work around https://github.com/python/typing/issues/241 + + +class DefaultDownloadProgressBar(BaseDownloadProgressBar, + _BaseBar): # type: ignore + pass + + +class DownloadSilentBar(BaseDownloadProgressBar, SilentBar): # type: ignore + pass + + +class DownloadIncrementalBar(BaseDownloadProgressBar, # type: ignore + IncrementalBar): + pass + + +class DownloadChargingBar(BaseDownloadProgressBar, # type: ignore + ChargingBar): + pass + + +class DownloadShadyBar(BaseDownloadProgressBar, ShadyBar): # type: ignore + pass + + +class DownloadFillingSquaresBar(BaseDownloadProgressBar, # type: ignore + FillingSquaresBar): + pass + + +class DownloadFillingCirclesBar(BaseDownloadProgressBar, # type: ignore + FillingCirclesBar): + pass + + +class DownloadBlueEmojiProgressBar(BaseDownloadProgressBar, # type: ignore + BlueEmojiBar): + pass + + +class DownloadProgressSpinner(WindowsMixin, InterruptibleMixin, + DownloadProgressMixin, WritelnMixin, Spinner): + + file = sys.stdout + suffix = "%(downloaded)s %(download_speed)s" + + def next_phase(self): + if not hasattr(self, "_phaser"): + self._phaser = itertools.cycle(self.phases) + return next(self._phaser) + + def update(self): + message = self.message % self + phase = self.next_phase() + suffix = self.suffix % self + line = ''.join([ + message, + " " if message else "", + phase, + " " if suffix else "", + suffix, + ]) + + self.writeln(line) + + +BAR_TYPES = { + "off": (DownloadSilentBar, DownloadSilentBar), + "on": (DefaultDownloadProgressBar, DownloadProgressSpinner), + "ascii": (DownloadIncrementalBar, DownloadProgressSpinner), + "pretty": (DownloadFillingCirclesBar, DownloadProgressSpinner), + "emoji": (DownloadBlueEmojiProgressBar, DownloadProgressSpinner) +} + + +def DownloadProgressProvider(progress_bar, max=None): + if max is None or max == 0: + return BAR_TYPES[progress_bar][1]().iter + else: + return BAR_TYPES[progress_bar][0](max=max).iter + + +################################################################ +# Generic "something is happening" spinners +# +# We don't even try using progress.spinner.Spinner here because it's actually +# simpler to reimplement from scratch than to coerce their code into doing +# what we need. +################################################################ + +@contextlib.contextmanager +def hidden_cursor(file): + # The Windows terminal does not support the hide/show cursor ANSI codes, + # even via colorama. So don't even try. + if WINDOWS: + yield + # We don't want to clutter the output with control characters if we're + # writing to a file, or if the user is running with --quiet. + # See https://github.com/pypa/pip/issues/3418 + elif not file.isatty() or logger.getEffectiveLevel() > logging.INFO: + yield + else: + file.write(HIDE_CURSOR) + try: + yield + finally: + file.write(SHOW_CURSOR) + + +class RateLimiter(object): + def __init__(self, min_update_interval_seconds): + self._min_update_interval_seconds = min_update_interval_seconds + self._last_update = 0 + + def ready(self): + now = time.time() + delta = now - self._last_update + return delta >= self._min_update_interval_seconds + + def reset(self): + self._last_update = time.time() + + +class InteractiveSpinner(object): + def __init__(self, message, file=None, spin_chars="-\\|/", + # Empirically, 8 updates/second looks nice + min_update_interval_seconds=0.125): + self._message = message + if file is None: + file = sys.stdout + self._file = file + self._rate_limiter = RateLimiter(min_update_interval_seconds) + self._finished = False + + self._spin_cycle = itertools.cycle(spin_chars) + + self._file.write(" " * get_indentation() + self._message + " ... ") + self._width = 0 + + def _write(self, status): + assert not self._finished + # Erase what we wrote before by backspacing to the beginning, writing + # spaces to overwrite the old text, and then backspacing again + backup = "\b" * self._width + self._file.write(backup + " " * self._width + backup) + # Now we have a blank slate to add our status + self._file.write(status) + self._width = len(status) + self._file.flush() + self._rate_limiter.reset() + + def spin(self): + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._write(next(self._spin_cycle)) + + def finish(self, final_status): + if self._finished: + return + self._write(final_status) + self._file.write("\n") + self._file.flush() + self._finished = True + + +# Used for dumb terminals, non-interactive installs (no tty), etc. +# We still print updates occasionally (once every 60 seconds by default) to +# act as a keep-alive for systems like Travis-CI that take lack-of-output as +# an indication that a task has frozen. +class NonInteractiveSpinner(object): + def __init__(self, message, min_update_interval_seconds=60): + self._message = message + self._finished = False + self._rate_limiter = RateLimiter(min_update_interval_seconds) + self._update("started") + + def _update(self, status): + assert not self._finished + self._rate_limiter.reset() + logger.info("%s: %s", self._message, status) + + def spin(self): + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._update("still running...") + + def finish(self, final_status): + if self._finished: + return + self._update("finished with status '%s'" % (final_status,)) + self._finished = True + + +@contextlib.contextmanager +def open_spinner(message): + # Interactive spinner goes directly to sys.stdout rather than being routed + # through the logging system, but it acts like it has level INFO, + # i.e. it's only displayed if we're at level INFO or better. + # Non-interactive spinner goes through the logging system, so it is always + # in sync with logging configuration. + if sys.stdout.isatty() and logger.getEffectiveLevel() <= logging.INFO: + spinner = InteractiveSpinner(message) + else: + spinner = NonInteractiveSpinner(message) + try: + with hidden_cursor(sys.stdout): + yield spinner + except KeyboardInterrupt: + spinner.finish("canceled") + raise + except Exception: + spinner.finish("error") + raise + else: + spinner.finish("done") diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/vcs/__init__.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/vcs/__init__.py new file mode 100644 index 0000000..794b35d --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/vcs/__init__.py @@ -0,0 +1,509 @@ +"""Handles all VCS (version control) support""" +from __future__ import absolute_import + +import errno +import logging +import os +import shutil +import sys + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.exceptions import BadCommand +from pip._internal.utils.misc import ( + display_path, backup_dir, call_subprocess, rmtree, ask_path_exists, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Dict, Optional, Tuple # noqa: F401 + from pip._internal.cli.base_command import Command # noqa: F401 + +__all__ = ['vcs', 'get_src_requirement'] + + +logger = logging.getLogger(__name__) + + +class RevOptions(object): + + """ + Encapsulates a VCS-specific revision to install, along with any VCS + install options. + + Instances of this class should be treated as if immutable. + """ + + def __init__(self, vcs, rev=None, extra_args=None): + """ + Args: + vcs: a VersionControl object. + rev: the name of the revision to install. + extra_args: a list of extra options. + """ + if extra_args is None: + extra_args = [] + + self.extra_args = extra_args + self.rev = rev + self.vcs = vcs + + def __repr__(self): + return '<RevOptions {}: rev={!r}>'.format(self.vcs.name, self.rev) + + @property + def arg_rev(self): + if self.rev is None: + return self.vcs.default_arg_rev + + return self.rev + + def to_args(self): + """ + Return the VCS-specific command arguments. + """ + args = [] + rev = self.arg_rev + if rev is not None: + args += self.vcs.get_base_rev_args(rev) + args += self.extra_args + + return args + + def to_display(self): + if not self.rev: + return '' + + return ' (to revision {})'.format(self.rev) + + def make_new(self, rev): + """ + Make a copy of the current instance, but with a new rev. + + Args: + rev: the name of the revision for the new object. + """ + return self.vcs.make_rev_options(rev, extra_args=self.extra_args) + + +class VcsSupport(object): + _registry = {} # type: Dict[str, Command] + schemes = ['ssh', 'git', 'hg', 'bzr', 'sftp', 'svn'] + + def __init__(self): + # Register more schemes with urlparse for various version control + # systems + urllib_parse.uses_netloc.extend(self.schemes) + # Python >= 2.7.4, 3.3 doesn't have uses_fragment + if getattr(urllib_parse, 'uses_fragment', None): + urllib_parse.uses_fragment.extend(self.schemes) + super(VcsSupport, self).__init__() + + def __iter__(self): + return self._registry.__iter__() + + @property + def backends(self): + return list(self._registry.values()) + + @property + def dirnames(self): + return [backend.dirname for backend in self.backends] + + @property + def all_schemes(self): + schemes = [] + for backend in self.backends: + schemes.extend(backend.schemes) + return schemes + + def register(self, cls): + if not hasattr(cls, 'name'): + logger.warning('Cannot register VCS %s', cls.__name__) + return + if cls.name not in self._registry: + self._registry[cls.name] = cls + logger.debug('Registered VCS backend: %s', cls.name) + + def unregister(self, cls=None, name=None): + if name in self._registry: + del self._registry[name] + elif cls in self._registry.values(): + del self._registry[cls.name] + else: + logger.warning('Cannot unregister because no class or name given') + + def get_backend_name(self, location): + """ + Return the name of the version control backend if found at given + location, e.g. vcs.get_backend_name('/path/to/vcs/checkout') + """ + for vc_type in self._registry.values(): + if vc_type.controls_location(location): + logger.debug('Determine that %s uses VCS: %s', + location, vc_type.name) + return vc_type.name + return None + + def get_backend(self, name): + name = name.lower() + if name in self._registry: + return self._registry[name] + + def get_backend_from_location(self, location): + vc_type = self.get_backend_name(location) + if vc_type: + return self.get_backend(vc_type) + return None + + +vcs = VcsSupport() + + +class VersionControl(object): + name = '' + dirname = '' + # List of supported schemes for this Version Control + schemes = () # type: Tuple[str, ...] + # Iterable of environment variable names to pass to call_subprocess(). + unset_environ = () # type: Tuple[str, ...] + default_arg_rev = None # type: Optional[str] + + def __init__(self, url=None, *args, **kwargs): + self.url = url + super(VersionControl, self).__init__(*args, **kwargs) + + def get_base_rev_args(self, rev): + """ + Return the base revision arguments for a vcs command. + + Args: + rev: the name of a revision to install. Cannot be None. + """ + raise NotImplementedError + + def make_rev_options(self, rev=None, extra_args=None): + """ + Return a RevOptions object. + + Args: + rev: the name of a revision to install. + extra_args: a list of extra options. + """ + return RevOptions(self, rev, extra_args=extra_args) + + def _is_local_repository(self, repo): + """ + posix absolute paths start with os.path.sep, + win32 ones start with drive (like c:\\folder) + """ + drive, tail = os.path.splitdrive(repo) + return repo.startswith(os.path.sep) or drive + + def export(self, location): + """ + Export the repository at the url to the destination location + i.e. only download the files, without vcs informations + """ + raise NotImplementedError + + def get_netloc_and_auth(self, netloc, scheme): + """ + Parse the repository URL's netloc, and return the new netloc to use + along with auth information. + + Args: + netloc: the original repository URL netloc. + scheme: the repository URL's scheme without the vcs prefix. + + This is mainly for the Subversion class to override, so that auth + information can be provided via the --username and --password options + instead of through the URL. For other subclasses like Git without + such an option, auth information must stay in the URL. + + Returns: (netloc, (username, password)). + """ + return netloc, (None, None) + + def get_url_rev_and_auth(self, url): + """ + Parse the repository URL to use, and return the URL, revision, + and auth info to use. + + Returns: (url, rev, (username, password)). + """ + scheme, netloc, path, query, frag = urllib_parse.urlsplit(url) + if '+' not in scheme: + raise ValueError( + "Sorry, {!r} is a malformed VCS url. " + "The format is <vcs>+<protocol>://<url>, " + "e.g. svn+http://myrepo/svn/MyApp#egg=MyApp".format(url) + ) + # Remove the vcs prefix. + scheme = scheme.split('+', 1)[1] + netloc, user_pass = self.get_netloc_and_auth(netloc, scheme) + rev = None + if '@' in path: + path, rev = path.rsplit('@', 1) + url = urllib_parse.urlunsplit((scheme, netloc, path, query, '')) + return url, rev, user_pass + + def make_rev_args(self, username, password): + """ + Return the RevOptions "extra arguments" to use in obtain(). + """ + return [] + + def get_url_rev_options(self, url): + """ + Return the URL and RevOptions object to use in obtain() and in + some cases export(), as a tuple (url, rev_options). + """ + url, rev, user_pass = self.get_url_rev_and_auth(url) + username, password = user_pass + extra_args = self.make_rev_args(username, password) + rev_options = self.make_rev_options(rev, extra_args=extra_args) + + return url, rev_options + + def normalize_url(self, url): + """ + Normalize a URL for comparison by unquoting it and removing any + trailing slash. + """ + return urllib_parse.unquote(url).rstrip('/') + + def compare_urls(self, url1, url2): + """ + Compare two repo URLs for identity, ignoring incidental differences. + """ + return (self.normalize_url(url1) == self.normalize_url(url2)) + + def fetch_new(self, dest, url, rev_options): + """ + Fetch a revision from a repository, in the case that this is the + first fetch from the repository. + + Args: + dest: the directory to fetch the repository to. + rev_options: a RevOptions object. + """ + raise NotImplementedError + + def switch(self, dest, url, rev_options): + """ + Switch the repo at ``dest`` to point to ``URL``. + + Args: + rev_options: a RevOptions object. + """ + raise NotImplementedError + + def update(self, dest, url, rev_options): + """ + Update an already-existing repo to the given ``rev_options``. + + Args: + rev_options: a RevOptions object. + """ + raise NotImplementedError + + def is_commit_id_equal(self, dest, name): + """ + Return whether the id of the current commit equals the given name. + + Args: + dest: the repository directory. + name: a string name. + """ + raise NotImplementedError + + def obtain(self, dest): + """ + Install or update in editable mode the package represented by this + VersionControl object. + + Args: + dest: the repository directory in which to install or update. + """ + url, rev_options = self.get_url_rev_options(self.url) + + if not os.path.exists(dest): + self.fetch_new(dest, url, rev_options) + return + + rev_display = rev_options.to_display() + if self.is_repository_directory(dest): + existing_url = self.get_url(dest) + if self.compare_urls(existing_url, url): + logger.debug( + '%s in %s exists, and has correct URL (%s)', + self.repo_name.title(), + display_path(dest), + url, + ) + if not self.is_commit_id_equal(dest, rev_options.rev): + logger.info( + 'Updating %s %s%s', + display_path(dest), + self.repo_name, + rev_display, + ) + self.update(dest, url, rev_options) + else: + logger.info('Skipping because already up-to-date.') + return + + logger.warning( + '%s %s in %s exists with URL %s', + self.name, + self.repo_name, + display_path(dest), + existing_url, + ) + prompt = ('(s)witch, (i)gnore, (w)ipe, (b)ackup ', + ('s', 'i', 'w', 'b')) + else: + logger.warning( + 'Directory %s already exists, and is not a %s %s.', + dest, + self.name, + self.repo_name, + ) + prompt = ('(i)gnore, (w)ipe, (b)ackup ', ('i', 'w', 'b')) + + logger.warning( + 'The plan is to install the %s repository %s', + self.name, + url, + ) + response = ask_path_exists('What to do? %s' % prompt[0], prompt[1]) + + if response == 'a': + sys.exit(-1) + + if response == 'w': + logger.warning('Deleting %s', display_path(dest)) + rmtree(dest) + self.fetch_new(dest, url, rev_options) + return + + if response == 'b': + dest_dir = backup_dir(dest) + logger.warning( + 'Backing up %s to %s', display_path(dest), dest_dir, + ) + shutil.move(dest, dest_dir) + self.fetch_new(dest, url, rev_options) + return + + # Do nothing if the response is "i". + if response == 's': + logger.info( + 'Switching %s %s to %s%s', + self.repo_name, + display_path(dest), + url, + rev_display, + ) + self.switch(dest, url, rev_options) + + def unpack(self, location): + """ + Clean up current location and download the url repository + (and vcs infos) into location + """ + if os.path.exists(location): + rmtree(location) + self.obtain(location) + + def get_src_requirement(self, dist, location): + """ + Return a string representing the requirement needed to + redownload the files currently present in location, something + like: + {repository_url}@{revision}#egg={project_name}-{version_identifier} + """ + raise NotImplementedError + + def get_url(self, location): + """ + Return the url used at location + """ + raise NotImplementedError + + def get_revision(self, location): + """ + Return the current commit id of the files at the given location. + """ + raise NotImplementedError + + def run_command(self, cmd, show_stdout=True, cwd=None, + on_returncode='raise', + command_desc=None, + extra_environ=None, spinner=None): + """ + Run a VCS subcommand + This is simply a wrapper around call_subprocess that adds the VCS + command name, and checks that the VCS is available + """ + cmd = [self.name] + cmd + try: + return call_subprocess(cmd, show_stdout, cwd, + on_returncode, + command_desc, extra_environ, + unset_environ=self.unset_environ, + spinner=spinner) + except OSError as e: + # errno.ENOENT = no such file or directory + # In other words, the VCS executable isn't available + if e.errno == errno.ENOENT: + raise BadCommand( + 'Cannot find command %r - do you have ' + '%r installed and in your ' + 'PATH?' % (self.name, self.name)) + else: + raise # re-raise exception if a different error occurred + + @classmethod + def is_repository_directory(cls, path): + """ + Return whether a directory path is a repository directory. + """ + logger.debug('Checking in %s for %s (%s)...', + path, cls.dirname, cls.name) + return os.path.exists(os.path.join(path, cls.dirname)) + + @classmethod + def controls_location(cls, location): + """ + Check if a location is controlled by the vcs. + It is meant to be overridden to implement smarter detection + mechanisms for specific vcs. + + This can do more than is_repository_directory() alone. For example, + the Git override checks that Git is actually available. + """ + return cls.is_repository_directory(location) + + +def get_src_requirement(dist, location): + version_control = vcs.get_backend_from_location(location) + if version_control: + try: + return version_control().get_src_requirement(dist, + location) + except BadCommand: + logger.warning( + 'cannot determine version of editable source in %s ' + '(%s command not found in path)', + location, + version_control.name, + ) + return dist.as_requirement() + logger.warning( + 'cannot determine version of editable source in %s (is not SVN ' + 'checkout, Git clone, Mercurial clone or Bazaar branch)', + location, + ) + return dist.as_requirement() diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/vcs/bazaar.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/vcs/bazaar.py new file mode 100644 index 0000000..3cc66c9 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/vcs/bazaar.py @@ -0,0 +1,112 @@ +from __future__ import absolute_import + +import logging +import os + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.download import path_to_url +from pip._internal.utils.misc import ( + display_path, make_vcs_requirement_url, rmtree, +) +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.vcs import VersionControl, vcs + +logger = logging.getLogger(__name__) + + +class Bazaar(VersionControl): + name = 'bzr' + dirname = '.bzr' + repo_name = 'branch' + schemes = ( + 'bzr', 'bzr+http', 'bzr+https', 'bzr+ssh', 'bzr+sftp', 'bzr+ftp', + 'bzr+lp', + ) + + def __init__(self, url=None, *args, **kwargs): + super(Bazaar, self).__init__(url, *args, **kwargs) + # This is only needed for python <2.7.5 + # Register lp but do not expose as a scheme to support bzr+lp. + if getattr(urllib_parse, 'uses_fragment', None): + urllib_parse.uses_fragment.extend(['lp']) + + def get_base_rev_args(self, rev): + return ['-r', rev] + + def export(self, location): + """ + Export the Bazaar repository at the url to the destination location + """ + # Remove the location to make sure Bazaar can export it correctly + if os.path.exists(location): + rmtree(location) + + with TempDirectory(kind="export") as temp_dir: + self.unpack(temp_dir.path) + + self.run_command( + ['export', location], + cwd=temp_dir.path, show_stdout=False, + ) + + def fetch_new(self, dest, url, rev_options): + rev_display = rev_options.to_display() + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + cmd_args = ['branch', '-q'] + rev_options.to_args() + [url, dest] + self.run_command(cmd_args) + + def switch(self, dest, url, rev_options): + self.run_command(['switch', url], cwd=dest) + + def update(self, dest, url, rev_options): + cmd_args = ['pull', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + + def get_url_rev_and_auth(self, url): + # hotfix the URL scheme after removing bzr+ from bzr+ssh:// readd it + url, rev, user_pass = super(Bazaar, self).get_url_rev_and_auth(url) + if url.startswith('ssh://'): + url = 'bzr+' + url + return url, rev, user_pass + + def get_url(self, location): + urls = self.run_command(['info'], show_stdout=False, cwd=location) + for line in urls.splitlines(): + line = line.strip() + for x in ('checkout of branch: ', + 'parent branch: '): + if line.startswith(x): + repo = line.split(x)[1] + if self._is_local_repository(repo): + return path_to_url(repo) + return repo + return None + + def get_revision(self, location): + revision = self.run_command( + ['revno'], show_stdout=False, cwd=location, + ) + return revision.splitlines()[-1] + + def get_src_requirement(self, dist, location): + repo = self.get_url(location) + if not repo: + return None + if not repo.lower().startswith('bzr:'): + repo = 'bzr+' + repo + current_rev = self.get_revision(location) + egg_project_name = dist.egg_name().split('-', 1)[0] + return make_vcs_requirement_url(repo, current_rev, egg_project_name) + + def is_commit_id_equal(self, dest, name): + """Always assume the versions don't match""" + return False + + +vcs.register(Bazaar) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/vcs/git.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/vcs/git.py new file mode 100644 index 0000000..9778539 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/vcs/git.py @@ -0,0 +1,346 @@ +from __future__ import absolute_import + +import logging +import os.path +import re + +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip._internal.exceptions import BadCommand +from pip._internal.utils.compat import samefile +from pip._internal.utils.misc import display_path, make_vcs_requirement_url +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.vcs import VersionControl, vcs + +urlsplit = urllib_parse.urlsplit +urlunsplit = urllib_parse.urlunsplit + + +logger = logging.getLogger(__name__) + + +HASH_REGEX = re.compile('[a-fA-F0-9]{40}') + + +def looks_like_hash(sha): + return bool(HASH_REGEX.match(sha)) + + +class Git(VersionControl): + name = 'git' + dirname = '.git' + repo_name = 'clone' + schemes = ( + 'git', 'git+http', 'git+https', 'git+ssh', 'git+git', 'git+file', + ) + # Prevent the user's environment variables from interfering with pip: + # https://github.com/pypa/pip/issues/1130 + unset_environ = ('GIT_DIR', 'GIT_WORK_TREE') + default_arg_rev = 'HEAD' + + def __init__(self, url=None, *args, **kwargs): + + # Works around an apparent Git bug + # (see https://article.gmane.org/gmane.comp.version-control.git/146500) + if url: + scheme, netloc, path, query, fragment = urlsplit(url) + if scheme.endswith('file'): + initial_slashes = path[:-len(path.lstrip('/'))] + newpath = ( + initial_slashes + + urllib_request.url2pathname(path) + .replace('\\', '/').lstrip('/') + ) + url = urlunsplit((scheme, netloc, newpath, query, fragment)) + after_plus = scheme.find('+') + 1 + url = scheme[:after_plus] + urlunsplit( + (scheme[after_plus:], netloc, newpath, query, fragment), + ) + + super(Git, self).__init__(url, *args, **kwargs) + + def get_base_rev_args(self, rev): + return [rev] + + def get_git_version(self): + VERSION_PFX = 'git version ' + version = self.run_command(['version'], show_stdout=False) + if version.startswith(VERSION_PFX): + version = version[len(VERSION_PFX):].split()[0] + else: + version = '' + # get first 3 positions of the git version becasue + # on windows it is x.y.z.windows.t, and this parses as + # LegacyVersion which always smaller than a Version. + version = '.'.join(version.split('.')[:3]) + return parse_version(version) + + def get_branch(self, location): + """ + Return the current branch, or None if HEAD isn't at a branch + (e.g. detached HEAD). + """ + args = ['rev-parse', '--abbrev-ref', 'HEAD'] + output = self.run_command(args, show_stdout=False, cwd=location) + branch = output.strip() + + if branch == 'HEAD': + return None + + return branch + + def export(self, location): + """Export the Git repository at the url to the destination location""" + if not location.endswith('/'): + location = location + '/' + + with TempDirectory(kind="export") as temp_dir: + self.unpack(temp_dir.path) + self.run_command( + ['checkout-index', '-a', '-f', '--prefix', location], + show_stdout=False, cwd=temp_dir.path + ) + + def get_revision_sha(self, dest, rev): + """ + Return (sha_or_none, is_branch), where sha_or_none is a commit hash + if the revision names a remote branch or tag, otherwise None. + + Args: + dest: the repository directory. + rev: the revision name. + """ + # Pass rev to pre-filter the list. + output = self.run_command(['show-ref', rev], cwd=dest, + show_stdout=False, on_returncode='ignore') + refs = {} + for line in output.strip().splitlines(): + try: + sha, ref = line.split() + except ValueError: + # Include the offending line to simplify troubleshooting if + # this error ever occurs. + raise ValueError('unexpected show-ref line: {!r}'.format(line)) + + refs[ref] = sha + + branch_ref = 'refs/remotes/origin/{}'.format(rev) + tag_ref = 'refs/tags/{}'.format(rev) + + sha = refs.get(branch_ref) + if sha is not None: + return (sha, True) + + sha = refs.get(tag_ref) + + return (sha, False) + + def resolve_revision(self, dest, url, rev_options): + """ + Resolve a revision to a new RevOptions object with the SHA1 of the + branch, tag, or ref if found. + + Args: + rev_options: a RevOptions object. + """ + rev = rev_options.arg_rev + sha, is_branch = self.get_revision_sha(dest, rev) + + if sha is not None: + rev_options = rev_options.make_new(sha) + rev_options.branch_name = rev if is_branch else None + + return rev_options + + # Do not show a warning for the common case of something that has + # the form of a Git commit hash. + if not looks_like_hash(rev): + logger.warning( + "Did not find branch or tag '%s', assuming revision or ref.", + rev, + ) + + if not rev.startswith('refs/'): + return rev_options + + # If it looks like a ref, we have to fetch it explicitly. + self.run_command( + ['fetch', '-q', url] + rev_options.to_args(), + cwd=dest, + ) + # Change the revision to the SHA of the ref we fetched + sha = self.get_revision(dest, rev='FETCH_HEAD') + rev_options = rev_options.make_new(sha) + + return rev_options + + def is_commit_id_equal(self, dest, name): + """ + Return whether the current commit hash equals the given name. + + Args: + dest: the repository directory. + name: a string name. + """ + if not name: + # Then avoid an unnecessary subprocess call. + return False + + return self.get_revision(dest) == name + + def fetch_new(self, dest, url, rev_options): + rev_display = rev_options.to_display() + logger.info( + 'Cloning %s%s to %s', url, rev_display, display_path(dest), + ) + self.run_command(['clone', '-q', url, dest]) + + if rev_options.rev: + # Then a specific revision was requested. + rev_options = self.resolve_revision(dest, url, rev_options) + branch_name = getattr(rev_options, 'branch_name', None) + if branch_name is None: + # Only do a checkout if the current commit id doesn't match + # the requested revision. + if not self.is_commit_id_equal(dest, rev_options.rev): + cmd_args = ['checkout', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + elif self.get_branch(dest) != branch_name: + # Then a specific branch was requested, and that branch + # is not yet checked out. + track_branch = 'origin/{}'.format(branch_name) + cmd_args = [ + 'checkout', '-b', branch_name, '--track', track_branch, + ] + self.run_command(cmd_args, cwd=dest) + + #: repo may contain submodules + self.update_submodules(dest) + + def switch(self, dest, url, rev_options): + self.run_command(['config', 'remote.origin.url', url], cwd=dest) + cmd_args = ['checkout', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + + self.update_submodules(dest) + + def update(self, dest, url, rev_options): + # First fetch changes from the default remote + if self.get_git_version() >= parse_version('1.9.0'): + # fetch tags in addition to everything else + self.run_command(['fetch', '-q', '--tags'], cwd=dest) + else: + self.run_command(['fetch', '-q'], cwd=dest) + # Then reset to wanted revision (maybe even origin/master) + rev_options = self.resolve_revision(dest, url, rev_options) + cmd_args = ['reset', '--hard', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + #: update submodules + self.update_submodules(dest) + + def get_url(self, location): + """Return URL of the first remote encountered.""" + remotes = self.run_command( + ['config', '--get-regexp', r'remote\..*\.url'], + show_stdout=False, cwd=location, + ) + remotes = remotes.splitlines() + found_remote = remotes[0] + for remote in remotes: + if remote.startswith('remote.origin.url '): + found_remote = remote + break + url = found_remote.split(' ')[1] + return url.strip() + + def get_revision(self, location, rev=None): + if rev is None: + rev = 'HEAD' + current_rev = self.run_command( + ['rev-parse', rev], show_stdout=False, cwd=location, + ) + return current_rev.strip() + + def _get_subdirectory(self, location): + """Return the relative path of setup.py to the git repo root.""" + # find the repo root + git_dir = self.run_command(['rev-parse', '--git-dir'], + show_stdout=False, cwd=location).strip() + if not os.path.isabs(git_dir): + git_dir = os.path.join(location, git_dir) + root_dir = os.path.join(git_dir, '..') + # find setup.py + orig_location = location + while not os.path.exists(os.path.join(location, 'setup.py')): + last_location = location + location = os.path.dirname(location) + if location == last_location: + # We've traversed up to the root of the filesystem without + # finding setup.py + logger.warning( + "Could not find setup.py for directory %s (tried all " + "parent directories)", + orig_location, + ) + return None + # relative path of setup.py to repo root + if samefile(root_dir, location): + return None + return os.path.relpath(location, root_dir) + + def get_src_requirement(self, dist, location): + repo = self.get_url(location) + if not repo.lower().startswith('git:'): + repo = 'git+' + repo + current_rev = self.get_revision(location) + egg_project_name = dist.egg_name().split('-', 1)[0] + subdir = self._get_subdirectory(location) + req = make_vcs_requirement_url(repo, current_rev, egg_project_name, + subdir=subdir) + + return req + + def get_url_rev_and_auth(self, url): + """ + Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'. + That's required because although they use SSH they sometimes don't + work with a ssh:// scheme (e.g. GitHub). But we need a scheme for + parsing. Hence we remove it again afterwards and return it as a stub. + """ + if '://' not in url: + assert 'file:' not in url + url = url.replace('git+', 'git+ssh://') + url, rev, user_pass = super(Git, self).get_url_rev_and_auth(url) + url = url.replace('ssh://', '') + else: + url, rev, user_pass = super(Git, self).get_url_rev_and_auth(url) + + return url, rev, user_pass + + def update_submodules(self, location): + if not os.path.exists(os.path.join(location, '.gitmodules')): + return + self.run_command( + ['submodule', 'update', '--init', '--recursive', '-q'], + cwd=location, + ) + + @classmethod + def controls_location(cls, location): + if super(Git, cls).controls_location(location): + return True + try: + r = cls().run_command(['rev-parse'], + cwd=location, + show_stdout=False, + on_returncode='ignore') + return not r + except BadCommand: + logger.debug("could not determine if %s is under git control " + "because git is not available", location) + return False + + +vcs.register(Git) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/vcs/mercurial.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/vcs/mercurial.py new file mode 100644 index 0000000..17cfb67 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/vcs/mercurial.py @@ -0,0 +1,101 @@ +from __future__ import absolute_import + +import logging +import os + +from pip._vendor.six.moves import configparser + +from pip._internal.download import path_to_url +from pip._internal.utils.misc import display_path, make_vcs_requirement_url +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.vcs import VersionControl, vcs + +logger = logging.getLogger(__name__) + + +class Mercurial(VersionControl): + name = 'hg' + dirname = '.hg' + repo_name = 'clone' + schemes = ('hg', 'hg+http', 'hg+https', 'hg+ssh', 'hg+static-http') + + def get_base_rev_args(self, rev): + return [rev] + + def export(self, location): + """Export the Hg repository at the url to the destination location""" + with TempDirectory(kind="export") as temp_dir: + self.unpack(temp_dir.path) + + self.run_command( + ['archive', location], show_stdout=False, cwd=temp_dir.path + ) + + def fetch_new(self, dest, url, rev_options): + rev_display = rev_options.to_display() + logger.info( + 'Cloning hg %s%s to %s', + url, + rev_display, + display_path(dest), + ) + self.run_command(['clone', '--noupdate', '-q', url, dest]) + cmd_args = ['update', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + + def switch(self, dest, url, rev_options): + repo_config = os.path.join(dest, self.dirname, 'hgrc') + config = configparser.SafeConfigParser() + try: + config.read(repo_config) + config.set('paths', 'default', url) + with open(repo_config, 'w') as config_file: + config.write(config_file) + except (OSError, configparser.NoSectionError) as exc: + logger.warning( + 'Could not switch Mercurial repository to %s: %s', url, exc, + ) + else: + cmd_args = ['update', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + + def update(self, dest, url, rev_options): + self.run_command(['pull', '-q'], cwd=dest) + cmd_args = ['update', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + + def get_url(self, location): + url = self.run_command( + ['showconfig', 'paths.default'], + show_stdout=False, cwd=location).strip() + if self._is_local_repository(url): + url = path_to_url(url) + return url.strip() + + def get_revision(self, location): + current_revision = self.run_command( + ['parents', '--template={rev}'], + show_stdout=False, cwd=location).strip() + return current_revision + + def get_revision_hash(self, location): + current_rev_hash = self.run_command( + ['parents', '--template={node}'], + show_stdout=False, cwd=location).strip() + return current_rev_hash + + def get_src_requirement(self, dist, location): + repo = self.get_url(location) + if not repo.lower().startswith('hg:'): + repo = 'hg+' + repo + current_rev_hash = self.get_revision_hash(location) + egg_project_name = dist.egg_name().split('-', 1)[0] + return make_vcs_requirement_url(repo, current_rev_hash, + egg_project_name) + + def is_commit_id_equal(self, dest, name): + """Always assume the versions don't match""" + return False + + +vcs.register(Mercurial) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/vcs/subversion.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/vcs/subversion.py new file mode 100644 index 0000000..6f7cb5d --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/vcs/subversion.py @@ -0,0 +1,213 @@ +from __future__ import absolute_import + +import logging +import os +import re + +from pip._internal.models.link import Link +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + display_path, make_vcs_requirement_url, rmtree, split_auth_from_netloc, +) +from pip._internal.vcs import VersionControl, vcs + +_svn_xml_url_re = re.compile('url="([^"]+)"') +_svn_rev_re = re.compile(r'committed-rev="(\d+)"') +_svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"') +_svn_info_xml_url_re = re.compile(r'<url>(.*)</url>') + + +logger = logging.getLogger(__name__) + + +class Subversion(VersionControl): + name = 'svn' + dirname = '.svn' + repo_name = 'checkout' + schemes = ('svn', 'svn+ssh', 'svn+http', 'svn+https', 'svn+svn') + + def get_base_rev_args(self, rev): + return ['-r', rev] + + def export(self, location): + """Export the svn repository at the url to the destination location""" + url, rev_options = self.get_url_rev_options(self.url) + + logger.info('Exporting svn repository %s to %s', url, location) + with indent_log(): + if os.path.exists(location): + # Subversion doesn't like to check out over an existing + # directory --force fixes this, but was only added in svn 1.5 + rmtree(location) + cmd_args = ['export'] + rev_options.to_args() + [url, location] + self.run_command(cmd_args, show_stdout=False) + + def fetch_new(self, dest, url, rev_options): + rev_display = rev_options.to_display() + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + cmd_args = ['checkout', '-q'] + rev_options.to_args() + [url, dest] + self.run_command(cmd_args) + + def switch(self, dest, url, rev_options): + cmd_args = ['switch'] + rev_options.to_args() + [url, dest] + self.run_command(cmd_args) + + def update(self, dest, url, rev_options): + cmd_args = ['update'] + rev_options.to_args() + [dest] + self.run_command(cmd_args) + + def get_location(self, dist, dependency_links): + for url in dependency_links: + egg_fragment = Link(url).egg_fragment + if not egg_fragment: + continue + if '-' in egg_fragment: + # FIXME: will this work when a package has - in the name? + key = '-'.join(egg_fragment.split('-')[:-1]).lower() + else: + key = egg_fragment + if key == dist.key: + return url.split('#', 1)[0] + return None + + def get_revision(self, location): + """ + Return the maximum revision for all files under a given location + """ + # Note: taken from setuptools.command.egg_info + revision = 0 + + for base, dirs, files in os.walk(location): + if self.dirname not in dirs: + dirs[:] = [] + continue # no sense walking uncontrolled subdirs + dirs.remove(self.dirname) + entries_fn = os.path.join(base, self.dirname, 'entries') + if not os.path.exists(entries_fn): + # FIXME: should we warn? + continue + + dirurl, localrev = self._get_svn_url_rev(base) + + if base == location: + base = dirurl + '/' # save the root url + elif not dirurl or not dirurl.startswith(base): + dirs[:] = [] + continue # not part of the same svn tree, skip it + revision = max(revision, localrev) + return revision + + def get_netloc_and_auth(self, netloc, scheme): + """ + This override allows the auth information to be passed to svn via the + --username and --password options instead of via the URL. + """ + if scheme == 'ssh': + # The --username and --password options can't be used for + # svn+ssh URLs, so keep the auth information in the URL. + return super(Subversion, self).get_netloc_and_auth( + netloc, scheme) + + return split_auth_from_netloc(netloc) + + def get_url_rev_and_auth(self, url): + # hotfix the URL scheme after removing svn+ from svn+ssh:// readd it + url, rev, user_pass = super(Subversion, self).get_url_rev_and_auth(url) + if url.startswith('ssh://'): + url = 'svn+' + url + return url, rev, user_pass + + def make_rev_args(self, username, password): + extra_args = [] + if username: + extra_args += ['--username', username] + if password: + extra_args += ['--password', password] + + return extra_args + + def get_url(self, location): + # In cases where the source is in a subdirectory, not alongside + # setup.py we have to look up in the location until we find a real + # setup.py + orig_location = location + while not os.path.exists(os.path.join(location, 'setup.py')): + last_location = location + location = os.path.dirname(location) + if location == last_location: + # We've traversed up to the root of the filesystem without + # finding setup.py + logger.warning( + "Could not find setup.py for directory %s (tried all " + "parent directories)", + orig_location, + ) + return None + + return self._get_svn_url_rev(location)[0] + + def _get_svn_url_rev(self, location): + from pip._internal.exceptions import InstallationError + + entries_path = os.path.join(location, self.dirname, 'entries') + if os.path.exists(entries_path): + with open(entries_path) as f: + data = f.read() + else: # subversion >= 1.7 does not have the 'entries' file + data = '' + + if (data.startswith('8') or + data.startswith('9') or + data.startswith('10')): + data = list(map(str.splitlines, data.split('\n\x0c\n'))) + del data[0][0] # get rid of the '8' + url = data[0][3] + revs = [int(d[9]) for d in data if len(d) > 9 and d[9]] + [0] + elif data.startswith('<?xml'): + match = _svn_xml_url_re.search(data) + if not match: + raise ValueError('Badly formatted data: %r' % data) + url = match.group(1) # get repository URL + revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)] + [0] + else: + try: + # subversion >= 1.7 + xml = self.run_command( + ['info', '--xml', location], + show_stdout=False, + ) + url = _svn_info_xml_url_re.search(xml).group(1) + revs = [ + int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml) + ] + except InstallationError: + url, revs = None, [] + + if revs: + rev = max(revs) + else: + rev = 0 + + return url, rev + + def get_src_requirement(self, dist, location): + repo = self.get_url(location) + if repo is None: + return None + repo = 'svn+' + repo + rev = self.get_revision(location) + # FIXME: why not project name? + egg_project_name = dist.egg_name().split('-', 1)[0] + return make_vcs_requirement_url(repo, rev, egg_project_name) + + def is_commit_id_equal(self, dest, name): + """Always assume the versions don't match""" + return False + + +vcs.register(Subversion) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_internal/wheel.py b/Display/.venv/lib/python3.7/site-packages/pip/_internal/wheel.py new file mode 100644 index 0000000..5ce890e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_internal/wheel.py @@ -0,0 +1,831 @@ +""" +Support for installing and building the "wheel" binary package format. +""" +from __future__ import absolute_import + +import collections +import compileall +import csv +import hashlib +import logging +import os.path +import re +import shutil +import stat +import sys +import warnings +from base64 import urlsafe_b64encode +from email.parser import Parser + +from pip._vendor import pkg_resources +from pip._vendor.distlib.scripts import ScriptMaker +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.six import StringIO + +from pip._internal import pep425tags +from pip._internal.download import path_to_url, unpack_url +from pip._internal.exceptions import ( + InstallationError, InvalidWheelFilename, UnsupportedWheel, +) +from pip._internal.locations import ( + PIP_DELETE_MARKER_FILENAME, distutils_scheme, +) +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + call_subprocess, captured_stdout, ensure_dir, read_chunks, +) +from pip._internal.utils.setuptools_build import SETUPTOOLS_SHIM +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.ui import open_spinner + +if MYPY_CHECK_RUNNING: + from typing import Dict, List, Optional # noqa: F401 + +wheel_ext = '.whl' + +VERSION_COMPATIBLE = (1, 0) + + +logger = logging.getLogger(__name__) + + +def rehash(path, blocksize=1 << 20): + """Return (hash, length) for path using hashlib.sha256()""" + h = hashlib.sha256() + length = 0 + with open(path, 'rb') as f: + for block in read_chunks(f, size=blocksize): + length += len(block) + h.update(block) + digest = 'sha256=' + urlsafe_b64encode( + h.digest() + ).decode('latin1').rstrip('=') + return (digest, length) + + +def open_for_csv(name, mode): + if sys.version_info[0] < 3: + nl = {} + bin = 'b' + else: + nl = {'newline': ''} + bin = '' + return open(name, mode + bin, **nl) + + +def fix_script(path): + """Replace #!python with #!/path/to/python + Return True if file was changed.""" + # XXX RECORD hashes will need to be updated + if os.path.isfile(path): + with open(path, 'rb') as script: + firstline = script.readline() + if not firstline.startswith(b'#!python'): + return False + exename = sys.executable.encode(sys.getfilesystemencoding()) + firstline = b'#!' + exename + os.linesep.encode("ascii") + rest = script.read() + with open(path, 'wb') as script: + script.write(firstline) + script.write(rest) + return True + + +dist_info_re = re.compile(r"""^(?P<namever>(?P<name>.+?)(-(?P<ver>.+?))?) + \.dist-info$""", re.VERBOSE) + + +def root_is_purelib(name, wheeldir): + """ + Return True if the extracted wheel in wheeldir should go into purelib. + """ + name_folded = name.replace("-", "_") + for item in os.listdir(wheeldir): + match = dist_info_re.match(item) + if match and match.group('name') == name_folded: + with open(os.path.join(wheeldir, item, 'WHEEL')) as wheel: + for line in wheel: + line = line.lower().rstrip() + if line == "root-is-purelib: true": + return True + return False + + +def get_entrypoints(filename): + if not os.path.exists(filename): + return {}, {} + + # This is done because you can pass a string to entry_points wrappers which + # means that they may or may not be valid INI files. The attempt here is to + # strip leading and trailing whitespace in order to make them valid INI + # files. + with open(filename) as fp: + data = StringIO() + for line in fp: + data.write(line.strip()) + data.write("\n") + data.seek(0) + + # get the entry points and then the script names + entry_points = pkg_resources.EntryPoint.parse_map(data) + console = entry_points.get('console_scripts', {}) + gui = entry_points.get('gui_scripts', {}) + + def _split_ep(s): + """get the string representation of EntryPoint, remove space and split + on '='""" + return str(s).replace(" ", "").split("=") + + # convert the EntryPoint objects into strings with module:function + console = dict(_split_ep(v) for v in console.values()) + gui = dict(_split_ep(v) for v in gui.values()) + return console, gui + + +def message_about_scripts_not_on_PATH(scripts): + # type: (List[str]) -> Optional[str] + """Determine if any scripts are not on PATH and format a warning. + + Returns a warning message if one or more scripts are not on PATH, + otherwise None. + """ + if not scripts: + return None + + # Group scripts by the path they were installed in + grouped_by_dir = collections.defaultdict(set) # type: Dict[str, set] + for destfile in scripts: + parent_dir = os.path.dirname(destfile) + script_name = os.path.basename(destfile) + grouped_by_dir[parent_dir].add(script_name) + + # We don't want to warn for directories that are on PATH. + not_warn_dirs = [ + os.path.normcase(i).rstrip(os.sep) for i in + os.environ.get("PATH", "").split(os.pathsep) + ] + # If an executable sits with sys.executable, we don't warn for it. + # This covers the case of venv invocations without activating the venv. + not_warn_dirs.append(os.path.normcase(os.path.dirname(sys.executable))) + warn_for = { + parent_dir: scripts for parent_dir, scripts in grouped_by_dir.items() + if os.path.normcase(parent_dir) not in not_warn_dirs + } + if not warn_for: + return None + + # Format a message + msg_lines = [] + for parent_dir, scripts in warn_for.items(): + scripts = sorted(scripts) + if len(scripts) == 1: + start_text = "script {} is".format(scripts[0]) + else: + start_text = "scripts {} are".format( + ", ".join(scripts[:-1]) + " and " + scripts[-1] + ) + + msg_lines.append( + "The {} installed in '{}' which is not on PATH." + .format(start_text, parent_dir) + ) + + last_line_fmt = ( + "Consider adding {} to PATH or, if you prefer " + "to suppress this warning, use --no-warn-script-location." + ) + if len(msg_lines) == 1: + msg_lines.append(last_line_fmt.format("this directory")) + else: + msg_lines.append(last_line_fmt.format("these directories")) + + # Returns the formatted multiline message + return "\n".join(msg_lines) + + +def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None, + pycompile=True, scheme=None, isolated=False, prefix=None, + warn_script_location=True): + """Install a wheel""" + + if not scheme: + scheme = distutils_scheme( + name, user=user, home=home, root=root, isolated=isolated, + prefix=prefix, + ) + + if root_is_purelib(name, wheeldir): + lib_dir = scheme['purelib'] + else: + lib_dir = scheme['platlib'] + + info_dir = [] + data_dirs = [] + source = wheeldir.rstrip(os.path.sep) + os.path.sep + + # Record details of the files moved + # installed = files copied from the wheel to the destination + # changed = files changed while installing (scripts #! line typically) + # generated = files newly generated during the install (script wrappers) + installed = {} + changed = set() + generated = [] + + # Compile all of the pyc files that we're going to be installing + if pycompile: + with captured_stdout() as stdout: + with warnings.catch_warnings(): + warnings.filterwarnings('ignore') + compileall.compile_dir(source, force=True, quiet=True) + logger.debug(stdout.getvalue()) + + def normpath(src, p): + return os.path.relpath(src, p).replace(os.path.sep, '/') + + def record_installed(srcfile, destfile, modified=False): + """Map archive RECORD paths to installation RECORD paths.""" + oldpath = normpath(srcfile, wheeldir) + newpath = normpath(destfile, lib_dir) + installed[oldpath] = newpath + if modified: + changed.add(destfile) + + def clobber(source, dest, is_base, fixer=None, filter=None): + ensure_dir(dest) # common for the 'include' path + + for dir, subdirs, files in os.walk(source): + basedir = dir[len(source):].lstrip(os.path.sep) + destdir = os.path.join(dest, basedir) + if is_base and basedir.split(os.path.sep, 1)[0].endswith('.data'): + continue + for s in subdirs: + destsubdir = os.path.join(dest, basedir, s) + if is_base and basedir == '' and destsubdir.endswith('.data'): + data_dirs.append(s) + continue + elif (is_base and + s.endswith('.dist-info') and + canonicalize_name(s).startswith( + canonicalize_name(req.name))): + assert not info_dir, ('Multiple .dist-info directories: ' + + destsubdir + ', ' + + ', '.join(info_dir)) + info_dir.append(destsubdir) + for f in files: + # Skip unwanted files + if filter and filter(f): + continue + srcfile = os.path.join(dir, f) + destfile = os.path.join(dest, basedir, f) + # directory creation is lazy and after the file filtering above + # to ensure we don't install empty dirs; empty dirs can't be + # uninstalled. + ensure_dir(destdir) + + # copyfile (called below) truncates the destination if it + # exists and then writes the new contents. This is fine in most + # cases, but can cause a segfault if pip has loaded a shared + # object (e.g. from pyopenssl through its vendored urllib3) + # Since the shared object is mmap'd an attempt to call a + # symbol in it will then cause a segfault. Unlinking the file + # allows writing of new contents while allowing the process to + # continue to use the old copy. + if os.path.exists(destfile): + os.unlink(destfile) + + # We use copyfile (not move, copy, or copy2) to be extra sure + # that we are not moving directories over (copyfile fails for + # directories) as well as to ensure that we are not copying + # over any metadata because we want more control over what + # metadata we actually copy over. + shutil.copyfile(srcfile, destfile) + + # Copy over the metadata for the file, currently this only + # includes the atime and mtime. + st = os.stat(srcfile) + if hasattr(os, "utime"): + os.utime(destfile, (st.st_atime, st.st_mtime)) + + # If our file is executable, then make our destination file + # executable. + if os.access(srcfile, os.X_OK): + st = os.stat(srcfile) + permissions = ( + st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH + ) + os.chmod(destfile, permissions) + + changed = False + if fixer: + changed = fixer(destfile) + record_installed(srcfile, destfile, changed) + + clobber(source, lib_dir, True) + + assert info_dir, "%s .dist-info directory not found" % req + + # Get the defined entry points + ep_file = os.path.join(info_dir[0], 'entry_points.txt') + console, gui = get_entrypoints(ep_file) + + def is_entrypoint_wrapper(name): + # EP, EP.exe and EP-script.py are scripts generated for + # entry point EP by setuptools + if name.lower().endswith('.exe'): + matchname = name[:-4] + elif name.lower().endswith('-script.py'): + matchname = name[:-10] + elif name.lower().endswith(".pya"): + matchname = name[:-4] + else: + matchname = name + # Ignore setuptools-generated scripts + return (matchname in console or matchname in gui) + + for datadir in data_dirs: + fixer = None + filter = None + for subdir in os.listdir(os.path.join(wheeldir, datadir)): + fixer = None + if subdir == 'scripts': + fixer = fix_script + filter = is_entrypoint_wrapper + source = os.path.join(wheeldir, datadir, subdir) + dest = scheme[subdir] + clobber(source, dest, False, fixer=fixer, filter=filter) + + maker = ScriptMaker(None, scheme['scripts']) + + # Ensure old scripts are overwritten. + # See https://github.com/pypa/pip/issues/1800 + maker.clobber = True + + # Ensure we don't generate any variants for scripts because this is almost + # never what somebody wants. + # See https://bitbucket.org/pypa/distlib/issue/35/ + maker.variants = {''} + + # This is required because otherwise distlib creates scripts that are not + # executable. + # See https://bitbucket.org/pypa/distlib/issue/32/ + maker.set_mode = True + + # Simplify the script and fix the fact that the default script swallows + # every single stack trace. + # See https://bitbucket.org/pypa/distlib/issue/34/ + # See https://bitbucket.org/pypa/distlib/issue/33/ + def _get_script_text(entry): + if entry.suffix is None: + raise InstallationError( + "Invalid script entry point: %s for req: %s - A callable " + "suffix is required. Cf https://packaging.python.org/en/" + "latest/distributing.html#console-scripts for more " + "information." % (entry, req) + ) + return maker.script_template % { + "module": entry.prefix, + "import_name": entry.suffix.split(".")[0], + "func": entry.suffix, + } + + maker._get_script_text = _get_script_text + maker.script_template = r"""# -*- coding: utf-8 -*- +import re +import sys + +from %(module)s import %(import_name)s + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(%(func)s()) +""" + + # Special case pip and setuptools to generate versioned wrappers + # + # The issue is that some projects (specifically, pip and setuptools) use + # code in setup.py to create "versioned" entry points - pip2.7 on Python + # 2.7, pip3.3 on Python 3.3, etc. But these entry points are baked into + # the wheel metadata at build time, and so if the wheel is installed with + # a *different* version of Python the entry points will be wrong. The + # correct fix for this is to enhance the metadata to be able to describe + # such versioned entry points, but that won't happen till Metadata 2.0 is + # available. + # In the meantime, projects using versioned entry points will either have + # incorrect versioned entry points, or they will not be able to distribute + # "universal" wheels (i.e., they will need a wheel per Python version). + # + # Because setuptools and pip are bundled with _ensurepip and virtualenv, + # we need to use universal wheels. So, as a stopgap until Metadata 2.0, we + # override the versioned entry points in the wheel and generate the + # correct ones. This code is purely a short-term measure until Metadata 2.0 + # is available. + # + # To add the level of hack in this section of code, in order to support + # ensurepip this code will look for an ``ENSUREPIP_OPTIONS`` environment + # variable which will control which version scripts get installed. + # + # ENSUREPIP_OPTIONS=altinstall + # - Only pipX.Y and easy_install-X.Y will be generated and installed + # ENSUREPIP_OPTIONS=install + # - pipX.Y, pipX, easy_install-X.Y will be generated and installed. Note + # that this option is technically if ENSUREPIP_OPTIONS is set and is + # not altinstall + # DEFAULT + # - The default behavior is to install pip, pipX, pipX.Y, easy_install + # and easy_install-X.Y. + pip_script = console.pop('pip', None) + if pip_script: + if "ENSUREPIP_OPTIONS" not in os.environ: + spec = 'pip = ' + pip_script + generated.extend(maker.make(spec)) + + if os.environ.get("ENSUREPIP_OPTIONS", "") != "altinstall": + spec = 'pip%s = %s' % (sys.version[:1], pip_script) + generated.extend(maker.make(spec)) + + spec = 'pip%s = %s' % (sys.version[:3], pip_script) + generated.extend(maker.make(spec)) + # Delete any other versioned pip entry points + pip_ep = [k for k in console if re.match(r'pip(\d(\.\d)?)?$', k)] + for k in pip_ep: + del console[k] + easy_install_script = console.pop('easy_install', None) + if easy_install_script: + if "ENSUREPIP_OPTIONS" not in os.environ: + spec = 'easy_install = ' + easy_install_script + generated.extend(maker.make(spec)) + + spec = 'easy_install-%s = %s' % (sys.version[:3], easy_install_script) + generated.extend(maker.make(spec)) + # Delete any other versioned easy_install entry points + easy_install_ep = [ + k for k in console if re.match(r'easy_install(-\d\.\d)?$', k) + ] + for k in easy_install_ep: + del console[k] + + # Generate the console and GUI entry points specified in the wheel + if len(console) > 0: + generated_console_scripts = maker.make_multiple( + ['%s = %s' % kv for kv in console.items()] + ) + generated.extend(generated_console_scripts) + + if warn_script_location: + msg = message_about_scripts_not_on_PATH(generated_console_scripts) + if msg is not None: + logger.warning(msg) + + if len(gui) > 0: + generated.extend( + maker.make_multiple( + ['%s = %s' % kv for kv in gui.items()], + {'gui': True} + ) + ) + + # Record pip as the installer + installer = os.path.join(info_dir[0], 'INSTALLER') + temp_installer = os.path.join(info_dir[0], 'INSTALLER.pip') + with open(temp_installer, 'wb') as installer_file: + installer_file.write(b'pip\n') + shutil.move(temp_installer, installer) + generated.append(installer) + + # Record details of all files installed + record = os.path.join(info_dir[0], 'RECORD') + temp_record = os.path.join(info_dir[0], 'RECORD.pip') + with open_for_csv(record, 'r') as record_in: + with open_for_csv(temp_record, 'w+') as record_out: + reader = csv.reader(record_in) + writer = csv.writer(record_out) + outrows = [] + for row in reader: + row[0] = installed.pop(row[0], row[0]) + if row[0] in changed: + row[1], row[2] = rehash(row[0]) + outrows.append(tuple(row)) + for f in generated: + digest, length = rehash(f) + outrows.append((normpath(f, lib_dir), digest, length)) + for f in installed: + outrows.append((installed[f], '', '')) + for row in sorted(outrows): + writer.writerow(row) + shutil.move(temp_record, record) + + +def wheel_version(source_dir): + """ + Return the Wheel-Version of an extracted wheel, if possible. + + Otherwise, return False if we couldn't parse / extract it. + """ + try: + dist = [d for d in pkg_resources.find_on_path(None, source_dir)][0] + + wheel_data = dist.get_metadata('WHEEL') + wheel_data = Parser().parsestr(wheel_data) + + version = wheel_data['Wheel-Version'].strip() + version = tuple(map(int, version.split('.'))) + return version + except Exception: + return False + + +def check_compatibility(version, name): + """ + Raises errors or warns if called with an incompatible Wheel-Version. + + Pip should refuse to install a Wheel-Version that's a major series + ahead of what it's compatible with (e.g 2.0 > 1.1); and warn when + installing a version only minor version ahead (e.g 1.2 > 1.1). + + version: a 2-tuple representing a Wheel-Version (Major, Minor) + name: name of wheel or package to raise exception about + + :raises UnsupportedWheel: when an incompatible Wheel-Version is given + """ + if not version: + raise UnsupportedWheel( + "%s is in an unsupported or invalid wheel" % name + ) + if version[0] > VERSION_COMPATIBLE[0]: + raise UnsupportedWheel( + "%s's Wheel-Version (%s) is not compatible with this version " + "of pip" % (name, '.'.join(map(str, version))) + ) + elif version > VERSION_COMPATIBLE: + logger.warning( + 'Installing from a newer Wheel-Version (%s)', + '.'.join(map(str, version)), + ) + + +class Wheel(object): + """A wheel file""" + + # TODO: maybe move the install code into this class + + wheel_file_re = re.compile( + r"""^(?P<namever>(?P<name>.+?)-(?P<ver>.*?)) + ((-(?P<build>\d[^-]*?))?-(?P<pyver>.+?)-(?P<abi>.+?)-(?P<plat>.+?) + \.whl|\.dist-info)$""", + re.VERBOSE + ) + + def __init__(self, filename): + """ + :raises InvalidWheelFilename: when the filename is invalid for a wheel + """ + wheel_info = self.wheel_file_re.match(filename) + if not wheel_info: + raise InvalidWheelFilename( + "%s is not a valid wheel filename." % filename + ) + self.filename = filename + self.name = wheel_info.group('name').replace('_', '-') + # we'll assume "_" means "-" due to wheel naming scheme + # (https://github.com/pypa/pip/issues/1150) + self.version = wheel_info.group('ver').replace('_', '-') + self.build_tag = wheel_info.group('build') + self.pyversions = wheel_info.group('pyver').split('.') + self.abis = wheel_info.group('abi').split('.') + self.plats = wheel_info.group('plat').split('.') + + # All the tag combinations from this file + self.file_tags = { + (x, y, z) for x in self.pyversions + for y in self.abis for z in self.plats + } + + def support_index_min(self, tags=None): + """ + Return the lowest index that one of the wheel's file_tag combinations + achieves in the supported_tags list e.g. if there are 8 supported tags, + and one of the file tags is first in the list, then return 0. Returns + None is the wheel is not supported. + """ + if tags is None: # for mock + tags = pep425tags.get_supported() + indexes = [tags.index(c) for c in self.file_tags if c in tags] + return min(indexes) if indexes else None + + def supported(self, tags=None): + """Is this wheel supported on this system?""" + if tags is None: # for mock + tags = pep425tags.get_supported() + return bool(set(tags).intersection(self.file_tags)) + + +class WheelBuilder(object): + """Build wheels from a RequirementSet.""" + + def __init__(self, finder, preparer, wheel_cache, + build_options=None, global_options=None, no_clean=False): + self.finder = finder + self.preparer = preparer + self.wheel_cache = wheel_cache + + self._wheel_dir = preparer.wheel_download_dir + + self.build_options = build_options or [] + self.global_options = global_options or [] + self.no_clean = no_clean + + def _build_one(self, req, output_dir, python_tag=None): + """Build one wheel. + + :return: The filename of the built wheel, or None if the build failed. + """ + # Install build deps into temporary directory (PEP 518) + with req.build_env: + return self._build_one_inside_env(req, output_dir, + python_tag=python_tag) + + def _build_one_inside_env(self, req, output_dir, python_tag=None): + with TempDirectory(kind="wheel") as temp_dir: + if self.__build_one(req, temp_dir.path, python_tag=python_tag): + try: + wheel_name = os.listdir(temp_dir.path)[0] + wheel_path = os.path.join(output_dir, wheel_name) + shutil.move( + os.path.join(temp_dir.path, wheel_name), wheel_path + ) + logger.info('Stored in directory: %s', output_dir) + return wheel_path + except Exception: + pass + # Ignore return, we can't do anything else useful. + self._clean_one(req) + return None + + def _base_setup_args(self, req): + # NOTE: Eventually, we'd want to also -S to the flags here, when we're + # isolating. Currently, it breaks Python in virtualenvs, because it + # relies on site.py to find parts of the standard library outside the + # virtualenv. + return [ + sys.executable, '-u', '-c', + SETUPTOOLS_SHIM % req.setup_py + ] + list(self.global_options) + + def __build_one(self, req, tempd, python_tag=None): + base_args = self._base_setup_args(req) + + spin_message = 'Running setup.py bdist_wheel for %s' % (req.name,) + with open_spinner(spin_message) as spinner: + logger.debug('Destination directory: %s', tempd) + wheel_args = base_args + ['bdist_wheel', '-d', tempd] \ + + self.build_options + + if python_tag is not None: + wheel_args += ["--python-tag", python_tag] + + try: + call_subprocess(wheel_args, cwd=req.setup_py_dir, + show_stdout=False, spinner=spinner) + return True + except Exception: + spinner.finish("error") + logger.error('Failed building wheel for %s', req.name) + return False + + def _clean_one(self, req): + base_args = self._base_setup_args(req) + + logger.info('Running setup.py clean for %s', req.name) + clean_args = base_args + ['clean', '--all'] + try: + call_subprocess(clean_args, cwd=req.source_dir, show_stdout=False) + return True + except Exception: + logger.error('Failed cleaning build dir for %s', req.name) + return False + + def build(self, requirements, session, autobuilding=False): + """Build wheels. + + :param unpack: If True, replace the sdist we built from with the + newly built wheel, in preparation for installation. + :return: True if all the wheels built correctly. + """ + from pip._internal import index + from pip._internal.models.link import Link + + building_is_possible = self._wheel_dir or ( + autobuilding and self.wheel_cache.cache_dir + ) + assert building_is_possible + + buildset = [] + format_control = self.finder.format_control + for req in requirements: + if req.constraint: + continue + if req.is_wheel: + if not autobuilding: + logger.info( + 'Skipping %s, due to already being wheel.', req.name, + ) + elif autobuilding and req.editable: + pass + elif autobuilding and not req.source_dir: + pass + elif autobuilding and req.link and not req.link.is_artifact: + # VCS checkout. Build wheel just for this run. + buildset.append((req, True)) + else: + ephem_cache = False + if autobuilding: + link = req.link + base, ext = link.splitext() + if index.egg_info_matches(base, None, link) is None: + # E.g. local directory. Build wheel just for this run. + ephem_cache = True + if "binary" not in format_control.get_allowed_formats( + canonicalize_name(req.name)): + logger.info( + "Skipping bdist_wheel for %s, due to binaries " + "being disabled for it.", req.name, + ) + continue + buildset.append((req, ephem_cache)) + + if not buildset: + return True + + # Build the wheels. + logger.info( + 'Building wheels for collected packages: %s', + ', '.join([req.name for (req, _) in buildset]), + ) + _cache = self.wheel_cache # shorter name + with indent_log(): + build_success, build_failure = [], [] + for req, ephem in buildset: + python_tag = None + if autobuilding: + python_tag = pep425tags.implementation_tag + if ephem: + output_dir = _cache.get_ephem_path_for_link(req.link) + else: + output_dir = _cache.get_path_for_link(req.link) + try: + ensure_dir(output_dir) + except OSError as e: + logger.warning("Building wheel for %s failed: %s", + req.name, e) + build_failure.append(req) + continue + else: + output_dir = self._wheel_dir + wheel_file = self._build_one( + req, output_dir, + python_tag=python_tag, + ) + if wheel_file: + build_success.append(req) + if autobuilding: + # XXX: This is mildly duplicative with prepare_files, + # but not close enough to pull out to a single common + # method. + # The code below assumes temporary source dirs - + # prevent it doing bad things. + if req.source_dir and not os.path.exists(os.path.join( + req.source_dir, PIP_DELETE_MARKER_FILENAME)): + raise AssertionError( + "bad source dir - missing marker") + # Delete the source we built the wheel from + req.remove_temporary_source() + # set the build directory again - name is known from + # the work prepare_files did. + req.source_dir = req.build_location( + self.preparer.build_dir + ) + # Update the link for this. + req.link = Link(path_to_url(wheel_file)) + assert req.link.is_wheel + # extract the wheel into the dir + unpack_url( + req.link, req.source_dir, None, False, + session=session, + ) + else: + build_failure.append(req) + + # notify success/failure + if build_success: + logger.info( + 'Successfully built %s', + ' '.join([req.name for req in build_success]), + ) + if build_failure: + logger.info( + 'Failed to build %s', + ' '.join([req.name for req in build_failure]), + ) + # Return True if all builds were successful + return len(build_failure) == 0 diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_vendor/__init__.py b/Display/.venv/lib/python3.7/site-packages/pip/_vendor/__init__.py new file mode 100644 index 0000000..07db110 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_vendor/__init__.py @@ -0,0 +1,114 @@ +""" +pip._vendor is for vendoring dependencies of pip to prevent needing pip to +depend on something external. + +Files inside of pip._vendor should be considered immutable and should only be +updated to versions from upstream. +""" +from __future__ import absolute_import + +import glob +import os.path +import sys + +# Downstream redistributors which have debundled our dependencies should also +# patch this value to be true. This will trigger the additional patching +# to cause things like "six" to be available as pip. +DEBUNDLED = True + +# By default, look in this directory for a bunch of .whl files which we will +# add to the beginning of sys.path before attempting to import anything. This +# is done to support downstream re-distributors like Debian and Fedora who +# wish to create their own Wheels for our dependencies to aid in debundling. +WHEEL_DIR = os.path.abspath(os.path.join(sys.prefix, 'share', 'python-wheels')) + + +# Define a small helper function to alias our vendored modules to the real ones +# if the vendored ones do not exist. This idea of this was taken from +# https://github.com/kennethreitz/requests/pull/2567. +def vendored(modulename): + vendored_name = "{0}.{1}".format(__name__, modulename) + + try: + __import__(vendored_name, globals(), locals(), level=0) + except ImportError: + try: + __import__(modulename, globals(), locals(), level=0) + except ImportError: + # We can just silently allow import failures to pass here. If we + # got to this point it means that ``import pip._vendor.whatever`` + # failed and so did ``import whatever``. Since we're importing this + # upfront in an attempt to alias imports, not erroring here will + # just mean we get a regular import error whenever pip *actually* + # tries to import one of these modules to use it, which actually + # gives us a better error message than we would have otherwise + # gotten. + pass + else: + sys.modules[vendored_name] = sys.modules[modulename] + base, head = vendored_name.rsplit(".", 1) + setattr(sys.modules[base], head, sys.modules[modulename]) + + +# If we're operating in a debundled setup, then we want to go ahead and trigger +# the aliasing of our vendored libraries as well as looking for wheels to add +# to our sys.path. This will cause all of this code to be a no-op typically +# however downstream redistributors can enable it in a consistent way across +# all platforms. +if DEBUNDLED: + # Actually look inside of WHEEL_DIR to find .whl files and add them to the + # front of our sys.path. + sys.path[:] = glob.glob(os.path.join(WHEEL_DIR, "*.whl")) + sys.path + + # Actually alias all of our vendored dependencies. + vendored("cachecontrol") + vendored("colorama") + vendored("distlib") + vendored("distro") + vendored("html5lib") + vendored("lockfile") + vendored("six") + vendored("six.moves") + vendored("six.moves.urllib") + vendored("six.moves.urllib.parse") + vendored("packaging") + vendored("packaging.version") + vendored("packaging.specifiers") + vendored("pkg_resources") + vendored("progress") + vendored("pytoml") + vendored("retrying") + vendored("requests") + vendored("requests.packages") + vendored("requests.packages.urllib3") + vendored("requests.packages.urllib3._collections") + vendored("requests.packages.urllib3.connection") + vendored("requests.packages.urllib3.connectionpool") + vendored("requests.packages.urllib3.contrib") + vendored("requests.packages.urllib3.contrib.ntlmpool") + vendored("requests.packages.urllib3.contrib.pyopenssl") + vendored("requests.packages.urllib3.exceptions") + vendored("requests.packages.urllib3.fields") + vendored("requests.packages.urllib3.filepost") + vendored("requests.packages.urllib3.packages") + try: + vendored("requests.packages.urllib3.packages.ordered_dict") + vendored("requests.packages.urllib3.packages.six") + except ImportError: + # Debian already unbundles these from requests. + pass + vendored("requests.packages.urllib3.packages.ssl_match_hostname") + vendored("requests.packages.urllib3.packages.ssl_match_hostname." + "_implementation") + vendored("requests.packages.urllib3.poolmanager") + vendored("requests.packages.urllib3.request") + vendored("requests.packages.urllib3.response") + vendored("requests.packages.urllib3.util") + vendored("requests.packages.urllib3.util.connection") + vendored("requests.packages.urllib3.util.request") + vendored("requests.packages.urllib3.util.response") + vendored("requests.packages.urllib3.util.retry") + vendored("requests.packages.urllib3.util.ssl_") + vendored("requests.packages.urllib3.util.timeout") + vendored("requests.packages.urllib3.util.url") + vendored("urllib3") diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/__init__.py b/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/__init__.py new file mode 100644 index 0000000..8beedea --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/__init__.py @@ -0,0 +1,4 @@ +"""Wrappers to build Python packages using PEP 517 hooks +""" + +__version__ = '0.2' diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/_in_process.py b/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/_in_process.py new file mode 100644 index 0000000..baa14d3 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/_in_process.py @@ -0,0 +1,182 @@ +"""This is invoked in a subprocess to call the build backend hooks. + +It expects: +- Command line args: hook_name, control_dir +- Environment variable: PEP517_BUILD_BACKEND=entry.point:spec +- control_dir/input.json: + - {"kwargs": {...}} + +Results: +- control_dir/output.json + - {"return_val": ...} +""" +from glob import glob +from importlib import import_module +import os +from os.path import join as pjoin +import re +import shutil +import sys + +# This is run as a script, not a module, so it can't do a relative import +import compat + +def _build_backend(): + """Find and load the build backend""" + ep = os.environ['PEP517_BUILD_BACKEND'] + mod_path, _, obj_path = ep.partition(':') + obj = import_module(mod_path) + if obj_path: + for path_part in obj_path.split('.'): + obj = getattr(obj, path_part) + return obj + +def get_requires_for_build_wheel(config_settings): + """Invoke the optional get_requires_for_build_wheel hook + + Returns [] if the hook is not defined. + """ + backend = _build_backend() + try: + hook = backend.get_requires_for_build_wheel + except AttributeError: + return [] + else: + return hook(config_settings) + +def prepare_metadata_for_build_wheel(metadata_directory, config_settings): + """Invoke optional prepare_metadata_for_build_wheel + + Implements a fallback by building a wheel if the hook isn't defined. + """ + backend = _build_backend() + try: + hook = backend.prepare_metadata_for_build_wheel + except AttributeError: + return _get_wheel_metadata_from_wheel(backend, metadata_directory, + config_settings) + else: + return hook(metadata_directory, config_settings) + +WHEEL_BUILT_MARKER = 'PEP517_ALREADY_BUILT_WHEEL' + +def _dist_info_files(whl_zip): + """Identify the .dist-info folder inside a wheel ZipFile.""" + res = [] + for path in whl_zip.namelist(): + m = re.match(r'[^/\\]+-[^/\\]+\.dist-info/', path) + if m: + res.append(path) + if res: + return res + raise Exception("No .dist-info folder found in wheel") + +def _get_wheel_metadata_from_wheel(backend, metadata_directory, config_settings): + """Build a wheel and extract the metadata from it. + + Fallback for when the build backend does not define the 'get_wheel_metadata' + hook. + """ + from zipfile import ZipFile + whl_basename = backend.build_wheel(metadata_directory, config_settings) + with open(os.path.join(metadata_directory, WHEEL_BUILT_MARKER), 'wb'): + pass # Touch marker file + + whl_file = os.path.join(metadata_directory, whl_basename) + with ZipFile(whl_file) as zipf: + dist_info = _dist_info_files(zipf) + zipf.extractall(path=metadata_directory, members=dist_info) + return dist_info[0].split('/')[0] + +def _find_already_built_wheel(metadata_directory): + """Check for a wheel already built during the get_wheel_metadata hook. + """ + if not metadata_directory: + return None + metadata_parent = os.path.dirname(metadata_directory) + if not os.path.isfile(pjoin(metadata_parent, WHEEL_BUILT_MARKER)): + return None + + whl_files = glob(os.path.join(metadata_parent, '*.whl')) + if not whl_files: + print('Found wheel built marker, but no .whl files') + return None + if len(whl_files) > 1: + print('Found multiple .whl files; unspecified behaviour. ' + 'Will call build_wheel.') + return None + + # Exactly one .whl file + return whl_files[0] + +def build_wheel(wheel_directory, config_settings, metadata_directory=None): + """Invoke the mandatory build_wheel hook. + + If a wheel was already built in the prepare_metadata_for_build_wheel fallback, this + will copy it rather than rebuilding the wheel. + """ + prebuilt_whl = _find_already_built_wheel(metadata_directory) + if prebuilt_whl: + shutil.copy2(prebuilt_whl, wheel_directory) + return os.path.basename(prebuilt_whl) + + return _build_backend().build_wheel(wheel_directory, config_settings, + metadata_directory) + + +def get_requires_for_build_sdist(config_settings): + """Invoke the optional get_requires_for_build_wheel hook + + Returns [] if the hook is not defined. + """ + backend = _build_backend() + try: + hook = backend.get_requires_for_build_sdist + except AttributeError: + return [] + else: + return hook(config_settings) + +class _DummyException(Exception): + """Nothing should ever raise this exception""" + +class GotUnsupportedOperation(Exception): + """For internal use when backend raises UnsupportedOperation""" + +def build_sdist(sdist_directory, config_settings): + """Invoke the mandatory build_sdist hook.""" + backend = _build_backend() + try: + return backend.build_sdist(sdist_directory, config_settings) + except getattr(backend, 'UnsupportedOperation', _DummyException): + raise GotUnsupportedOperation + +HOOK_NAMES = { + 'get_requires_for_build_wheel', + 'prepare_metadata_for_build_wheel', + 'build_wheel', + 'get_requires_for_build_sdist', + 'build_sdist', +} + +def main(): + if len(sys.argv) < 3: + sys.exit("Needs args: hook_name, control_dir") + hook_name = sys.argv[1] + control_dir = sys.argv[2] + if hook_name not in HOOK_NAMES: + sys.exit("Unknown hook: %s" % hook_name) + hook = globals()[hook_name] + + hook_input = compat.read_json(pjoin(control_dir, 'input.json')) + + json_out = {'unsupported': False, 'return_val': None} + try: + json_out['return_val'] = hook(**hook_input['kwargs']) + except GotUnsupportedOperation: + json_out['unsupported'] = True + + compat.write_json(json_out, pjoin(control_dir, 'output.json'), indent=2) + +if __name__ == '__main__': + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/check.py b/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/check.py new file mode 100644 index 0000000..c65d51c --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/check.py @@ -0,0 +1,194 @@ +"""Check a project and backend by attempting to build using PEP 517 hooks. +""" +import argparse +import logging +import os +from os.path import isfile, join as pjoin +from pip._vendor.pytoml import TomlError, load as toml_load +import shutil +from subprocess import CalledProcessError +import sys +import tarfile +from tempfile import mkdtemp +import zipfile + +from .colorlog import enable_colourful_output +from .envbuild import BuildEnvironment +from .wrappers import Pep517HookCaller + +log = logging.getLogger(__name__) + +def check_build_sdist(hooks): + with BuildEnvironment() as env: + try: + env.pip_install(hooks.build_sys_requires) + log.info('Installed static build dependencies') + except CalledProcessError: + log.error('Failed to install static build dependencies') + return False + + try: + reqs = hooks.get_requires_for_build_sdist({}) + log.info('Got build requires: %s', reqs) + except: + log.error('Failure in get_requires_for_build_sdist', exc_info=True) + return False + + try: + env.pip_install(reqs) + log.info('Installed dynamic build dependencies') + except CalledProcessError: + log.error('Failed to install dynamic build dependencies') + return False + + td = mkdtemp() + log.info('Trying to build sdist in %s', td) + try: + try: + filename = hooks.build_sdist(td, {}) + log.info('build_sdist returned %r', filename) + except: + log.info('Failure in build_sdist', exc_info=True) + return False + + if not filename.endswith('.tar.gz'): + log.error("Filename %s doesn't have .tar.gz extension", filename) + return False + + path = pjoin(td, filename) + if isfile(path): + log.info("Output file %s exists", path) + else: + log.error("Output file %s does not exist", path) + return False + + if tarfile.is_tarfile(path): + log.info("Output file is a tar file") + else: + log.error("Output file is not a tar file") + return False + + finally: + shutil.rmtree(td) + + return True + +def check_build_wheel(hooks): + with BuildEnvironment() as env: + try: + env.pip_install(hooks.build_sys_requires) + log.info('Installed static build dependencies') + except CalledProcessError: + log.error('Failed to install static build dependencies') + return False + + try: + reqs = hooks.get_requires_for_build_wheel({}) + log.info('Got build requires: %s', reqs) + except: + log.error('Failure in get_requires_for_build_sdist', exc_info=True) + return False + + try: + env.pip_install(reqs) + log.info('Installed dynamic build dependencies') + except CalledProcessError: + log.error('Failed to install dynamic build dependencies') + return False + + td = mkdtemp() + log.info('Trying to build wheel in %s', td) + try: + try: + filename = hooks.build_wheel(td, {}) + log.info('build_wheel returned %r', filename) + except: + log.info('Failure in build_wheel', exc_info=True) + return False + + if not filename.endswith('.whl'): + log.error("Filename %s doesn't have .whl extension", filename) + return False + + path = pjoin(td, filename) + if isfile(path): + log.info("Output file %s exists", path) + else: + log.error("Output file %s does not exist", path) + return False + + if zipfile.is_zipfile(path): + log.info("Output file is a zip file") + else: + log.error("Output file is not a zip file") + return False + + finally: + shutil.rmtree(td) + + return True + + +def check(source_dir): + pyproject = pjoin(source_dir, 'pyproject.toml') + if isfile(pyproject): + log.info('Found pyproject.toml') + else: + log.error('Missing pyproject.toml') + return False + + try: + with open(pyproject) as f: + pyproject_data = toml_load(f) + # Ensure the mandatory data can be loaded + buildsys = pyproject_data['build-system'] + requires = buildsys['requires'] + backend = buildsys['build-backend'] + log.info('Loaded pyproject.toml') + except (TomlError, KeyError): + log.error("Invalid pyproject.toml", exc_info=True) + return False + + hooks = Pep517HookCaller(source_dir, backend) + + sdist_ok = check_build_sdist(hooks) + wheel_ok = check_build_wheel(hooks) + + if not sdist_ok: + log.warning('Sdist checks failed; scroll up to see') + if not wheel_ok: + log.warning('Wheel checks failed') + + return sdist_ok + + +def main(argv=None): + ap = argparse.ArgumentParser() + ap.add_argument('source_dir', + help="A directory containing pyproject.toml") + args = ap.parse_args(argv) + + enable_colourful_output() + + ok = check(args.source_dir) + + if ok: + print(ansi('Checks passed', 'green')) + else: + print(ansi('Checks failed', 'red')) + sys.exit(1) + +ansi_codes = { + 'reset': '\x1b[0m', + 'bold': '\x1b[1m', + 'red': '\x1b[31m', + 'green': '\x1b[32m', +} +def ansi(s, attr): + if os.name != 'nt' and sys.stdout.isatty(): + return ansi_codes[attr] + str(s) + ansi_codes['reset'] + else: + return str(s) + +if __name__ == '__main__': + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/colorlog.py b/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/colorlog.py new file mode 100644 index 0000000..26cf748 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/colorlog.py @@ -0,0 +1,110 @@ +"""Nicer log formatting with colours. + +Code copied from Tornado, Apache licensed. +""" +# Copyright 2012 Facebook +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging +import sys + +try: + import curses +except ImportError: + curses = None + +def _stderr_supports_color(): + color = False + if curses and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty(): + try: + curses.setupterm() + if curses.tigetnum("colors") > 0: + color = True + except Exception: + pass + return color + +class LogFormatter(logging.Formatter): + """Log formatter with colour support + """ + DEFAULT_COLORS = { + logging.INFO: 2, # Green + logging.WARNING: 3, # Yellow + logging.ERROR: 1, # Red + logging.CRITICAL: 1, + } + + def __init__(self, color=True, datefmt=None): + r""" + :arg bool color: Enables color support. + :arg string fmt: Log message format. + It will be applied to the attributes dict of log records. The + text between ``%(color)s`` and ``%(end_color)s`` will be colored + depending on the level if color support is on. + :arg dict colors: color mappings from logging level to terminal color + code + :arg string datefmt: Datetime format. + Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``. + .. versionchanged:: 3.2 + Added ``fmt`` and ``datefmt`` arguments. + """ + logging.Formatter.__init__(self, datefmt=datefmt) + self._colors = {} + if color and _stderr_supports_color(): + # The curses module has some str/bytes confusion in + # python3. Until version 3.2.3, most methods return + # bytes, but only accept strings. In addition, we want to + # output these strings with the logging module, which + # works with unicode strings. The explicit calls to + # unicode() below are harmless in python2 but will do the + # right conversion in python 3. + fg_color = (curses.tigetstr("setaf") or + curses.tigetstr("setf") or "") + if (3, 0) < sys.version_info < (3, 2, 3): + fg_color = str(fg_color, "ascii") + + for levelno, code in self.DEFAULT_COLORS.items(): + self._colors[levelno] = str(curses.tparm(fg_color, code), "ascii") + self._normal = str(curses.tigetstr("sgr0"), "ascii") + + scr = curses.initscr() + self.termwidth = scr.getmaxyx()[1] + curses.endwin() + else: + self._normal = '' + # Default width is usually 80, but too wide is worse than too narrow + self.termwidth = 70 + + def formatMessage(self, record): + l = len(record.message) + right_text = '{initial}-{name}'.format(initial=record.levelname[0], + name=record.name) + if l + len(right_text) < self.termwidth: + space = ' ' * (self.termwidth - (l + len(right_text))) + else: + space = ' ' + + if record.levelno in self._colors: + start_color = self._colors[record.levelno] + end_color = self._normal + else: + start_color = end_color = '' + + return record.message + space + start_color + right_text + end_color + +def enable_colourful_output(level=logging.INFO): + handler = logging.StreamHandler() + handler.setFormatter(LogFormatter()) + logging.root.addHandler(handler) + logging.root.setLevel(level) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/compat.py b/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/compat.py new file mode 100644 index 0000000..01c66fc --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/compat.py @@ -0,0 +1,23 @@ +"""Handle reading and writing JSON in UTF-8, on Python 3 and 2.""" +import json +import sys + +if sys.version_info[0] >= 3: + # Python 3 + def write_json(obj, path, **kwargs): + with open(path, 'w', encoding='utf-8') as f: + json.dump(obj, f, **kwargs) + + def read_json(path): + with open(path, 'r', encoding='utf-8') as f: + return json.load(f) + +else: + # Python 2 + def write_json(obj, path, **kwargs): + with open(path, 'wb') as f: + json.dump(obj, f, encoding='utf-8', **kwargs) + + def read_json(path): + with open(path, 'rb') as f: + return json.load(f) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/envbuild.py b/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/envbuild.py new file mode 100644 index 0000000..c264f46 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/envbuild.py @@ -0,0 +1,150 @@ +"""Build wheels/sdists by installing build deps to a temporary environment. +""" + +import os +import logging +from pip._vendor import pytoml +import shutil +from subprocess import check_call +import sys +from sysconfig import get_paths +from tempfile import mkdtemp + +from .wrappers import Pep517HookCaller + +log = logging.getLogger(__name__) + +def _load_pyproject(source_dir): + with open(os.path.join(source_dir, 'pyproject.toml')) as f: + pyproject_data = pytoml.load(f) + buildsys = pyproject_data['build-system'] + return buildsys['requires'], buildsys['build-backend'] + + +class BuildEnvironment(object): + """Context manager to install build deps in a simple temporary environment + + Based on code I wrote for pip, which is MIT licensed. + """ + # Copyright (c) 2008-2016 The pip developers (see AUTHORS.txt file) + # + # Permission is hereby granted, free of charge, to any person obtaining + # a copy of this software and associated documentation files (the + # "Software"), to deal in the Software without restriction, including + # without limitation the rights to use, copy, modify, merge, publish, + # distribute, sublicense, and/or sell copies of the Software, and to + # permit persons to whom the Software is furnished to do so, subject to + # the following conditions: + # + # The above copyright notice and this permission notice shall be + # included in all copies or substantial portions of the Software. + # + # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + path = None + + def __init__(self, cleanup=True): + self._cleanup = cleanup + + def __enter__(self): + self.path = mkdtemp(prefix='pep517-build-env-') + log.info('Temporary build environment: %s', self.path) + + self.save_path = os.environ.get('PATH', None) + self.save_pythonpath = os.environ.get('PYTHONPATH', None) + + install_scheme = 'nt' if (os.name == 'nt') else 'posix_prefix' + install_dirs = get_paths(install_scheme, vars={ + 'base': self.path, + 'platbase': self.path, + }) + + scripts = install_dirs['scripts'] + if self.save_path: + os.environ['PATH'] = scripts + os.pathsep + self.save_path + else: + os.environ['PATH'] = scripts + os.pathsep + os.defpath + + if install_dirs['purelib'] == install_dirs['platlib']: + lib_dirs = install_dirs['purelib'] + else: + lib_dirs = install_dirs['purelib'] + os.pathsep + \ + install_dirs['platlib'] + if self.save_pythonpath: + os.environ['PYTHONPATH'] = lib_dirs + os.pathsep + \ + self.save_pythonpath + else: + os.environ['PYTHONPATH'] = lib_dirs + + return self + + def pip_install(self, reqs): + """Install dependencies into this env by calling pip in a subprocess""" + if not reqs: + return + log.info('Calling pip to install %s', reqs) + check_call([sys.executable, '-m', 'pip', 'install', '--ignore-installed', + '--prefix', self.path] + list(reqs)) + + def __exit__(self, exc_type, exc_val, exc_tb): + if self._cleanup and (self.path is not None) and os.path.isdir(self.path): + shutil.rmtree(self.path) + + if self.save_path is None: + os.environ.pop('PATH', None) + else: + os.environ['PATH'] = self.save_path + + if self.save_pythonpath is None: + os.environ.pop('PYTHONPATH', None) + else: + os.environ['PYTHONPATH'] = self.save_pythonpath + +def build_wheel(source_dir, wheel_dir, config_settings=None): + """Build a wheel from a source directory using PEP 517 hooks. + + :param str source_dir: Source directory containing pyproject.toml + :param str wheel_dir: Target directory to create wheel in + :param dict config_settings: Options to pass to build backend + + This is a blocking function which will run pip in a subprocess to install + build requirements. + """ + if config_settings is None: + config_settings = {} + requires, backend = _load_pyproject(source_dir) + hooks = Pep517HookCaller(source_dir, backend) + + with BuildEnvironment() as env: + env.pip_install(requires) + reqs = hooks.get_requires_for_build_wheel(config_settings) + env.pip_install(reqs) + return hooks.build_wheel(wheel_dir, config_settings) + + +def build_sdist(source_dir, sdist_dir, config_settings=None): + """Build an sdist from a source directory using PEP 517 hooks. + + :param str source_dir: Source directory containing pyproject.toml + :param str sdist_dir: Target directory to place sdist in + :param dict config_settings: Options to pass to build backend + + This is a blocking function which will run pip in a subprocess to install + build requirements. + """ + if config_settings is None: + config_settings = {} + requires, backend = _load_pyproject(source_dir) + hooks = Pep517HookCaller(source_dir, backend) + + with BuildEnvironment() as env: + env.pip_install(requires) + reqs = hooks.get_requires_for_build_sdist(config_settings) + env.pip_install(reqs) + return hooks.build_sdist(sdist_dir, config_settings) diff --git a/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/wrappers.py b/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/wrappers.py new file mode 100644 index 0000000..28260f3 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pip/_vendor/pep517/wrappers.py @@ -0,0 +1,134 @@ +from contextlib import contextmanager +import os +from os.path import dirname, abspath, join as pjoin +import shutil +from subprocess import check_call +import sys +from tempfile import mkdtemp + +from . import compat + +_in_proc_script = pjoin(dirname(abspath(__file__)), '_in_process.py') + +@contextmanager +def tempdir(): + td = mkdtemp() + try: + yield td + finally: + shutil.rmtree(td) + +class UnsupportedOperation(Exception): + """May be raised by build_sdist if the backend indicates that it can't.""" + +class Pep517HookCaller(object): + """A wrapper around a source directory to be built with a PEP 517 backend. + + source_dir : The path to the source directory, containing pyproject.toml. + backend : The build backend spec, as per PEP 517, from pyproject.toml. + """ + def __init__(self, source_dir, build_backend): + self.source_dir = abspath(source_dir) + self.build_backend = build_backend + + def get_requires_for_build_wheel(self, config_settings=None): + """Identify packages required for building a wheel + + Returns a list of dependency specifications, e.g.: + ["wheel >= 0.25", "setuptools"] + + This does not include requirements specified in pyproject.toml. + It returns the result of calling the equivalently named hook in a + subprocess. + """ + return self._call_hook('get_requires_for_build_wheel', { + 'config_settings': config_settings + }) + + def prepare_metadata_for_build_wheel(self, metadata_directory, config_settings=None): + """Prepare a *.dist-info folder with metadata for this project. + + Returns the name of the newly created folder. + + If the build backend defines a hook with this name, it will be called + in a subprocess. If not, the backend will be asked to build a wheel, + and the dist-info extracted from that. + """ + return self._call_hook('prepare_metadata_for_build_wheel', { + 'metadata_directory': abspath(metadata_directory), + 'config_settings': config_settings, + }) + + def build_wheel(self, wheel_directory, config_settings=None, metadata_directory=None): + """Build a wheel from this project. + + Returns the name of the newly created file. + + In general, this will call the 'build_wheel' hook in the backend. + However, if that was previously called by + 'prepare_metadata_for_build_wheel', and the same metadata_directory is + used, the previously built wheel will be copied to wheel_directory. + """ + if metadata_directory is not None: + metadata_directory = abspath(metadata_directory) + return self._call_hook('build_wheel', { + 'wheel_directory': abspath(wheel_directory), + 'config_settings': config_settings, + 'metadata_directory': metadata_directory, + }) + + def get_requires_for_build_sdist(self, config_settings=None): + """Identify packages required for building a wheel + + Returns a list of dependency specifications, e.g.: + ["setuptools >= 26"] + + This does not include requirements specified in pyproject.toml. + It returns the result of calling the equivalently named hook in a + subprocess. + """ + return self._call_hook('get_requires_for_build_sdist', { + 'config_settings': config_settings + }) + + def build_sdist(self, sdist_directory, config_settings=None): + """Build an sdist from this project. + + Returns the name of the newly created file. + + This calls the 'build_sdist' backend hook in a subprocess. + """ + return self._call_hook('build_sdist', { + 'sdist_directory': abspath(sdist_directory), + 'config_settings': config_settings, + }) + + + def _call_hook(self, hook_name, kwargs): + env = os.environ.copy() + + # On Python 2, pytoml returns Unicode values (which is correct) but the + # environment passed to check_call needs to contain string values. We + # convert here by encoding using ASCII (the backend can only contain + # letters, digits and _, . and : characters, and will be used as a + # Python identifier, so non-ASCII content is wrong on Python 2 in + # any case). + if sys.version_info[0] == 2: + build_backend = self.build_backend.encode('ASCII') + else: + build_backend = self.build_backend + + env['PEP517_BUILD_BACKEND'] = build_backend + with tempdir() as td: + compat.write_json({'kwargs': kwargs}, pjoin(td, 'input.json'), + indent=2) + + # Run the hook in a subprocess + check_call([sys.executable, _in_proc_script, hook_name, td], + cwd=self.source_dir, env=env) + + data = compat.read_json(pjoin(td, 'output.json')) + if data.get('unsupported'): + raise UnsupportedOperation + return data['return_val'] + diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/AUTHORS.txt b/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/AUTHORS.txt new file mode 100644 index 0000000..e845ac7 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/AUTHORS.txt @@ -0,0 +1,421 @@ +Adam Chainz <adam@adamj.eu> +Adam Wentz <awentz@theonion.com> +Adrien Morison <adrien.morison@gmail.com> +Alan Yee <alyee@ucsd.edu> +Aleks Bunin <github@compuix.com> +Alex Gaynor <alex.gaynor@gmail.com> +Alex Grönholm <alex.gronholm@nextday.fi> +Alex Morega <alex@grep.ro> +Alex Stachowiak <alexander@computer.org> +Alexander Shtyrov <rawzausho@gmail.com> +Alexandre Conrad <alexandre.conrad@gmail.com> +Alli <alzeih@users.noreply.github.com> +Anatoly Techtonik <techtonik@gmail.com> +Andrei Geacar <andrei.geacar@gmail.com> +Andrew Gaul <andrew@gaul.org> +Andrey Bulgakov <mail@andreiko.ru> +Andrés Delfino <34587441+andresdelfino@users.noreply.github.com> +Andrés Delfino <adelfino@gmail.com> +Andy Freeland <andy.freeland@redjack.com> +Andy Kluger <AndydeCleyre@users.noreply.github.com> +Anish Tambe <anish.tambe@yahoo.in> +Anrs Hu <anrs@douban.com> +Anthony Sottile <asottile@umich.edu> +Antoine Musso <hashar@free.fr> +Anton Ovchinnikov <revolver112@gmail.com> +Anton Patrushev <apatrushev@gmail.com> +Antonio Alvarado Hernandez <tnotstar@gmail.com> +Antony Lee <anntzer.lee@gmail.com> +Antti Kaihola <akaihol+github@ambitone.com> +Anubhav Patel <anubhavp28@gmail.com> +Anuj Godase <godaseanuj@gmail.com> +AQNOUCH Mohammed <aqnouch.mohammed@gmail.com> +AraHaan <seandhunt_7@yahoo.com> +Arindam Choudhury <arindam@live.com> +Armin Ronacher <armin.ronacher@active-4.com> +Ashley Manton <ajd.manton@googlemail.com> +Atsushi Odagiri <aodagx@gmail.com> +Avner Cohen <israbirding@gmail.com> +Baptiste Mispelon <bmispelon@gmail.com> +Barney Gale <barney.gale@gmail.com> +barneygale <barney.gale@gmail.com> +Bartek Ogryczak <b.ogryczak@gmail.com> +Bastian Venthur <mail@venthur.de> +Ben Darnell <ben@bendarnell.com> +Ben Hoyt <benhoyt@gmail.com> +Ben Rosser <rosser.bjr@gmail.com> +Bence Nagy <bence@underyx.me> +Benjamin VanEvery <ben@simondata.com> +Benoit Pierre <benoit.pierre@gmail.com> +Berker Peksag <berker.peksag@gmail.com> +Bernardo B. Marques <bernardo.fire@gmail.com> +Bernhard M. Wiedemann <bwiedemann@suse.de> +Bogdan Opanchuk <bogdan@opanchuk.net> +Brad Erickson <eosrei@gmail.com> +Bradley Ayers <bradley.ayers@gmail.com> +Brandon L. Reiss <brandon@damyata.co> +Brett Randall <javabrett@gmail.com> +Brian Rosner <brosner@gmail.com> +BrownTruck <BrownTruck@users.noreply.github.com> +Bruno Oliveira <nicoddemus@gmail.com> +Bruno Renié <brutasse@gmail.com> +Bstrdsmkr <bstrdsmkr@gmail.com> +Buck Golemon <buck@yelp.com> +burrows <burrows@preveil.com> +Bussonnier Matthias <bussonniermatthias@gmail.com> +c22 <c22@users.noreply.github.com> +Calvin Smith <eukaryote@users.noreply.github.com> +Carl Meyer <carl@oddbird.net> +Carlos Liam <carlos@aarzee.me> +Carol Willing <carolcode@willingconsulting.com> +Carter Thayer <carterwthayer@gmail.com> +Cass <cass.petrus@gmail.com> +Chandrasekhar Atina <chandu.atina@gmail.com> +Chris Brinker <chris.brinker@gmail.com> +Chris Jerdonek <chris.jerdonek@gmail.com> +Chris McDonough <chrism@plope.com> +Chris Wolfe <chriswwolfe@gmail.com> +Christian Heimes <christian@python.org> +Christian Oudard <christian.oudard@gmail.com> +Christopher Snyder <cnsnyder@users.noreply.github.com> +Clark Boylan <clark.boylan@gmail.com> +Clay McClure <clay@daemons.net> +Cody <Purring@users.noreply.github.com> +Cody Soyland <codysoyland@gmail.com> +Colin Watson <cjwatson@debian.org> +Connor Osborn <cdosborn@email.arizona.edu> +Cooper Lees <me@cooperlees.com> +Cooper Ry Lees <me@cooperlees.com> +Cory Benfield <lukasaoz@gmail.com> +Cory Wright <corywright@gmail.com> +Craig Kerstiens <craig.kerstiens@gmail.com> +Cristian Sorinel <cristian.sorinel@gmail.com> +Curtis Doty <Curtis@GreenKey.net> +Damian Quiroga <qdamian@gmail.com> +Dan Black <dyspop@gmail.com> +Dan Savilonis <djs@n-cube.org> +Dan Sully <daniel-github@electricrain.com> +daniel <mcdonaldd@unimelb.edu.au> +Daniel Collins <accounts@dac.io> +Daniel Hahler <git@thequod.de> +Daniel Holth <dholth@fastmail.fm> +Daniel Jost <torpedojost@gmail.com> +Daniel Shaulov <daniel.shaulov@gmail.com> +Daniele Procida <daniele@vurt.org> +Danny Hermes <daniel.j.hermes@gmail.com> +Dav Clark <davclark@gmail.com> +Dave Abrahams <dave@boostpro.com> +David Aguilar <davvid@gmail.com> +David Black <db@d1b.org> +David Caro <david@dcaro.es> +David Evans <d@drhevans.com> +David Linke <dr.david.linke@gmail.com> +David Pursehouse <david.pursehouse@gmail.com> +David Tucker <david@tucker.name> +David Wales <daviewales@gmail.com> +Davidovich <david.genest@gmail.com> +derwolfe <chriswwolfe@gmail.com> +Dmitry Gladkov <dmitry.gladkov@gmail.com> +Domen Kožar <domen@dev.si> +Donald Stufft <donald@stufft.io> +Dongweiming <dongweiming@admaster.com.cn> +Douglas Thor <dougthor42@users.noreply.github.com> +DrFeathers <WilliamGeorgeBurgess@gmail.com> +Dustin Ingram <di@di.codes> +Dwayne Bailey <dwayne@translate.org.za> +Ed Morley <501702+edmorley@users.noreply.github.com> +Ed Morley <emorley@mozilla.com> +Eli Schwartz <eschwartz93@gmail.com> +Emil Styrke <emil.styrke@gmail.com> +Endoh Takanao <djmchl@gmail.com> +enoch <lanxenet@gmail.com> +Eric Gillingham <Gillingham@bikezen.net> +Eric Hanchrow <eric.hanchrow@gmail.com> +Eric Hopper <hopper@omnifarious.org> +Erik M. Bray <embray@stsci.edu> +Erik Rose <erik@mozilla.com> +Ernest W Durbin III <ewdurbin@gmail.com> +Ernest W. Durbin III <ewdurbin@gmail.com> +Erwin Janssen <erwinjanssen@outlook.com> +Eugene Vereshchagin <evvers@gmail.com> +fiber-space <fiber-space@users.noreply.github.com> +Filip Kokosiński <filip.kokosinski@gmail.com> +Florian Briand <ownerfrance+github@hotmail.com> +Francesco <f.guerrieri@gmail.com> +Francesco Montesano <franz.bergesund@gmail.com> +Gabriel Curio <g.curio@gmail.com> +Gabriel de Perthuis <g2p.code@gmail.com> +Garry Polley <garrympolley@gmail.com> +gdanielson <graeme.danielson@gmail.com> +Geoffrey Lehée <geoffrey@lehee.name> +Geoffrey Sneddon <me@gsnedders.com> +George Song <george@55minutes.com> +Georgi Valkov <georgi.t.valkov@gmail.com> +Giftlin Rajaiah <giftlin.rgn@gmail.com> +gizmoguy1 <gizmoguy1@gmail.com> +gkdoc <40815324+gkdoc@users.noreply.github.com> +GOTO Hayato <3532528+gh640@users.noreply.github.com> +Guilherme Espada <porcariadagata@gmail.com> +Guy Rozendorn <guy@rzn.co.il> +Hari Charan <hcharan997@gmail.com> +Herbert Pfennig <herbert@albinen.com> +Hsiaoming Yang <lepture@me.com> +Hugo <hugovk@users.noreply.github.com> +Hugo Lopes Tavares <hltbra@gmail.com> +hugovk <hugovk@users.noreply.github.com> +Hynek Schlawack <hs@ox.cx> +Ian Bicking <ianb@colorstudy.com> +Ian Cordasco <graffatcolmingov@gmail.com> +Ian Lee <IanLee1521@gmail.com> +Ian Stapleton Cordasco <graffatcolmingov@gmail.com> +Ian Wienand <ian@wienand.org> +Ian Wienand <iwienand@redhat.com> +Igor Kuzmitshov <kuzmiigo@gmail.com> +Igor Sobreira <igor@igorsobreira.com> +Ilya Baryshev <baryshev@gmail.com> +INADA Naoki <songofacandy@gmail.com> +Ionel Cristian Mărieș <contact@ionelmc.ro> +Ionel Maries Cristian <ionel.mc@gmail.com> +Jakub Stasiak <kuba.stasiak@gmail.com> +Jakub Vysoky <jakub@borka.cz> +Jakub Wilk <jwilk@jwilk.net> +James Cleveland <jamescleveland@gmail.com> +James Cleveland <radiosilence@users.noreply.github.com> +James Firth <hello@james-firth.com> +James Polley <jp@jamezpolley.com> +Jan Pokorný <jpokorny@redhat.com> +Jannis Leidel <jannis@leidel.info> +jarondl <me@jarondl.net> +Jason R. Coombs <jaraco@jaraco.com> +Jay Graves <jay@skabber.com> +Jean-Christophe Fillion-Robin <jchris.fillionr@kitware.com> +Jeff Barber <jbarber@computer.org> +Jeff Dairiki <dairiki@dairiki.org> +Jeremy Stanley <fungi@yuggoth.org> +Jeremy Zafran <jzafran@users.noreply.github.com> +Jim Garrison <jim@garrison.cc> +Jivan Amara <Development@JivanAmara.net> +John-Scott Atlakson <john.scott.atlakson@gmail.com> +Jon Banafato <jon@jonafato.com> +Jon Dufresne <jon.dufresne@gmail.com> +Jon Parise <jon@indelible.org> +Jon Wayne Parrott <jjramone13@gmail.com> +Jonas Nockert <jonasnockert@gmail.com> +Jonathan Herbert <foohyfooh@gmail.com> +Joost Molenaar <j.j.molenaar@gmail.com> +Jorge Niedbalski <niedbalski@gmail.com> +Joseph Long <jdl@fastmail.fm> +Josh Bronson <jabronson@gmail.com> +Josh Hansen <josh@skwash.net> +Josh Schneier <josh.schneier@gmail.com> +Julien Demoor <julien@jdemoor.com> +jwg4 <jack.grahl@yahoo.co.uk> +Jyrki Pulliainen <jyrki@spotify.com> +Kamal Bin Mustafa <kamal@smach.net> +kaustav haldar <hi@kaustav.me> +keanemind <keanemind@gmail.com> +Kelsey Hightower <kelsey.hightower@gmail.com> +Kenneth Belitzky <kenny@belitzky.com> +Kenneth Reitz <me@kennethreitz.com> +Kenneth Reitz <me@kennethreitz.org> +Kevin Burke <kev@inburke.com> +Kevin Carter <kevin.carter@rackspace.com> +Kevin Frommelt <kevin.frommelt@webfilings.com> +Kexuan Sun <me@kianasun.com> +Kit Randel <kit@nocturne.net.nz> +kpinc <kop@meme.com> +Kumar McMillan <kumar.mcmillan@gmail.com> +Kyle Persohn <kyle.persohn@gmail.com> +Laurent Bristiel <laurent@bristiel.com> +Laurie Opperman <laurie@sitesee.com.au> +Leon Sasson <leonsassonha@gmail.com> +Lev Givon <lev@columbia.edu> +Lincoln de Sousa <lincoln@comum.org> +Lipis <lipiridis@gmail.com> +Loren Carvalho <lcarvalho@linkedin.com> +Lucas Cimon <lucas.cimon@gmail.com> +Ludovic Gasc <gmludo@gmail.com> +Luke Macken <lmacken@redhat.com> +Luo Jiebin <luo.jiebin@qq.com> +luojiebin <luojiebin@users.noreply.github.com> +luz.paz <luzpaz@users.noreply.github.com> +Marc Abramowitz <marc@marc-abramowitz.com> +Marc Tamlyn <marc.tamlyn@gmail.com> +Marcus Smith <qwcode@gmail.com> +Mariatta <Mariatta@users.noreply.github.com> +Mark Kohler <mark.kohler@proteinsimple.com> +Markus Hametner <fin+github@xbhd.org> +Masklinn <bitbucket.org@masklinn.net> +Matej Stuchlik <mstuchli@redhat.com> +Mathew Jennings <mjennings@foursquare.com> +Mathieu Bridon <bochecha@daitauha.fr> +Matt Good <matt@matt-good.net> +Matt Maker <trip@monstro.us> +Matt Robenolt <matt@ydekproductions.com> +matthew <matthew@trumbell.net> +Matthew Einhorn <moiein2000@gmail.com> +Matthew Gilliard <matthew.gilliard@gmail.com> +Matthew Iversen <teh.ivo@gmail.com> +Matthew Trumbell <matthew@thirdstonepartners.com> +Matthew Willson <matthew@swiftkey.com> +Matthias Bussonnier <bussonniermatthias@gmail.com> +mattip <matti.picus@gmail.com> +Maxim Kurnikov <maxim.kurnikov@gmail.com> +Maxime Rouyrre <rouyrre+git@gmail.com> +memoselyk <memoselyk@gmail.com> +Michael <michael-k@users.noreply.github.com> +Michael Aquilina <michaelaquilina@gmail.com> +Michael E. Karpeles <michael.karpeles@gmail.com> +Michael Klich <michal@michalklich.com> +Michael Williamson <mike@zwobble.org> +michaelpacer <michaelpacer@gmail.com> +Mickaël Schoentgen <mschoentgen@nuxeo.com> +Miguel Araujo Perez <miguel.araujo.perez@gmail.com> +Mihir Singh <git.service@mihirsingh.com> +Min RK <benjaminrk@gmail.com> +MinRK <benjaminrk@gmail.com> +Miro Hrončok <miro@hroncok.cz> +montefra <franz.bergesund@gmail.com> +Monty Taylor <mordred@inaugust.com> +Nate Coraor <nate@bx.psu.edu> +Nathaniel J. Smith <njs@pobox.com> +Nehal J Wani <nehaljw.kkd1@gmail.com> +Nick Coghlan <ncoghlan@gmail.com> +Nick Stenning <nick@whiteink.com> +Nikhil Benesch <nikhil.benesch@gmail.com> +Nitesh Sharma <nbsharma@outlook.com> +Nowell Strite <nowell@strite.org> +nvdv <modestdev@gmail.com> +Ofekmeister <ofekmeister@gmail.com> +Oliver Jeeves <oliver.jeeves@ocado.com> +Oliver Tonnhofer <olt@bogosoft.com> +Olivier Girardot <ssaboum@gmail.com> +Olivier Grisel <olivier.grisel@ensta.org> +Ollie Rutherfurd <orutherfurd@gmail.com> +OMOTO Kenji <k-omoto@m3.com> +Oren Held <orenhe@il.ibm.com> +Oscar Benjamin <oscar.j.benjamin@gmail.com> +Oz N Tiram <oz.tiram@gmail.com> +Patrick Dubroy <pdubroy@gmail.com> +Patrick Jenkins <patrick@socialgrowthtechnologies.com> +Patrick Lawson <pl@foursquare.com> +patricktokeeffe <patricktokeeffe@users.noreply.github.com> +Paul Kehrer <paul.l.kehrer@gmail.com> +Paul Moore <p.f.moore@gmail.com> +Paul Nasrat <pnasrat@gmail.com> +Paul Oswald <pauloswald@gmail.com> +Paul van der Linden <mail@paultjuh.org> +Paulus Schoutsen <paulus@paulusschoutsen.nl> +Pawel Jasinski <pawel.jasinski@gmail.com> +Pekka Klärck <peke@iki.fi> +Peter Waller <peter.waller@gmail.com> +Phaneendra Chiruvella <hi@pcx.io> +Phil Freo <phil@philfreo.com> +Phil Pennock <phil@pennock-tech.com> +Phil Whelan <phil123@gmail.com> +Philip Molloy <pamolloy@users.noreply.github.com> +Philippe Ombredanne <pombredanne@gmail.com> +Pi Delport <pjdelport@gmail.com> +Pierre-Yves Rofes <github@rofes.fr> +pip <pypa-dev@googlegroups.com> +Pradyun Gedam <pradyunsg@gmail.com> +Pratik Mallya <mallya@us.ibm.com> +Preston Holmes <preston@ptone.com> +Przemek Wrzos <hetmankp@none> +Qiangning Hong <hongqn@gmail.com> +R. David Murray <rdmurray@bitdance.com> +Rafael Caricio <rafael.jacinto@gmail.com> +Ralf Schmitt <ralf@systemexit.de> +Razzi Abuissa <razzi53@gmail.com> +Remi Rampin <remirampin@gmail.com> +Rene Dudfield <renesd@gmail.com> +Richard Jones <r1chardj0n3s@gmail.com> +RobberPhex <robberphex@gmail.com> +Robert Collins <rbtcollins@hp.com> +Robert McGibbon <rmcgibbo@gmail.com> +Robert T. McGibbon <rmcgibbo@gmail.com> +Roey Berman <roey.berman@gmail.com> +Rohan Jain <crodjer@gmail.com> +Rohan Jain <crodjer@users.noreply.github.com> +Rohan Jain <mail@rohanjain.in> +Roman Bogorodskiy <roman.bogorodskiy@ericsson.com> +Romuald Brunet <romuald@chivil.com> +Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> +Rory McCann <rory@technomancy.org> +Ross Brattain <ross.b.brattain@intel.com> +Roy Wellington Ⅳ <cactus_hugged@yahoo.com> +Roy Wellington Ⅳ <roy@mybasis.com> +Ryan Wooden <rygwdn@gmail.com> +ryneeverett <ryneeverett@gmail.com> +Sachi King <nakato@nakato.io> +Salvatore Rinchiera <salvatore@rinchiera.com> +schlamar <marc.schlaich@gmail.com> +Scott Kitterman <sklist@kitterman.com> +seanj <seanj@xyke.com> +Sebastian Schaetz <sschaetz@butterflynetinc.com> +Segev Finer <segev208@gmail.com> +Sergey Vasilyev <nolar@nolar.info> +Seth Woodworth <seth@sethish.com> +Shlomi Fish <shlomif@shlomifish.org> +Simeon Visser <svisser@users.noreply.github.com> +Simon Cross <hodgestar@gmail.com> +Simon Pichugin <simon.pichugin@gmail.com> +Sorin Sbarnea <sorin.sbarnea@gmail.com> +Stavros Korokithakis <stavros@korokithakis.net> +Stefan Scherfke <stefan@sofa-rockers.org> +Stephan Erb <github@stephanerb.eu> +stepshal <nessento@openmailbox.org> +Steve (Gadget) Barnes <gadgetsteve@hotmail.com> +Steve Barnes <gadgetsteve@hotmail.com> +Steve Kowalik <steven@wedontsleep.org> +Steven Myint <git@stevenmyint.com> +stonebig <stonebig34@gmail.com> +Stéphane Bidoul (ACSONE) <stephane.bidoul@acsone.eu> +Stéphane Bidoul <stephane.bidoul@acsone.eu> +Stéphane Klein <contact@stephane-klein.info> +Takayuki SHIMIZUKAWA <shimizukawa@gmail.com> +Thijs Triemstra <info@collab.nl> +Thomas Fenzl <thomas.fenzl@gmail.com> +Thomas Grainger <tagrain@gmail.com> +Thomas Guettler <tguettler@tbz-pariv.de> +Thomas Johansson <devnull@localhost> +Thomas Kluyver <thomas@kluyver.me.uk> +Thomas Smith <smithtg@ncbi.nlm.nih.gov> +Tim D. Smith <github@tim-smith.us> +Tim Harder <radhermit@gmail.com> +Tim Heap <tim@timheap.me> +tim smith <github@tim-smith.us> +tinruufu <tinruufu@gmail.com> +Tom Freudenheim <tom.freudenheim@onepeloton.com> +Tom V <tom@viner.tv> +Tomer Chachamu <tomer.chachamu@gmail.com> +Tony Zhaocheng Tan <tony@tonytan.io> +Toshio Kuratomi <toshio@fedoraproject.org> +Travis Swicegood <development@domain51.com> +Tzu-ping Chung <uranusjr@gmail.com> +Valentin Haenel <valentin.haenel@gmx.de> +Victor Stinner <victor.stinner@gmail.com> +Viktor Szépe <viktor@szepe.net> +Ville Skyttä <ville.skytta@iki.fi> +Vinay Sajip <vinay_sajip@yahoo.co.uk> +Vincent Philippon <sindaewoh@gmail.com> +Vitaly Babiy <vbabiy86@gmail.com> +Vladimir Rutsky <rutsky@users.noreply.github.com> +W. Trevor King <wking@drexel.edu> +Wil Tan <wil@dready.org> +Wilfred Hughes <me@wilfred.me.uk> +William ML Leslie <william.leslie.ttg@gmail.com> +Wolfgang Maier <wolfgang.maier@biologie.uni-freiburg.de> +Xavier Fernandez <xav.fernandez@gmail.com> +Xavier Fernandez <xavier.fernandez@polyconseil.fr> +xoviat <xoviat@users.noreply.github.com> +YAMAMOTO Takashi <yamamoto@midokura.com> +Yen Chi Hsuan <yan12125@gmail.com> +Yoval P <yoval@gmx.com> +Yu Jian <askingyj@gmail.com> +Zearin <zearin@gonk.net> +Zearin <Zearin@users.noreply.github.com> +Zhiping Deng <kofreestyler@gmail.com> +Zvezdan Petkovic <zpetkovic@acm.org> +Łukasz Langa <lukasz@langa.pl> +Семён Марьясин <simeon@maryasin.name> diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/INSTALLER b/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/LICENSE.txt b/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/LICENSE.txt new file mode 100644 index 0000000..d3379fa --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2008-2018 The pip developers (see AUTHORS.txt file) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/METADATA b/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/METADATA new file mode 100644 index 0000000..cf6c930 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/METADATA @@ -0,0 +1,13 @@ +Metadata-Version: 2.1 +Name: pkg_resources +Version: 0.0.0 +Summary: UNKNOWN +Home-page: UNKNOWN +Author: UNKNOWN +Author-email: UNKNOWN +License: UNKNOWN +Platform: UNKNOWN + +UNKNOWN + + diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/RECORD b/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/RECORD new file mode 100644 index 0000000..7938414 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/RECORD @@ -0,0 +1,38 @@ +pkg_resources-0.0.0.dist-info/AUTHORS.txt,sha256=Pu4WdZapZ2U2wKwWxd830ZxnROCHwmV_TpWoL9dqJ-M,15880 +pkg_resources-0.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pkg_resources-0.0.0.dist-info/LICENSE.txt,sha256=ORqHhOMZ2uVDFHfUzJvFBPxdcf2eieHIDxzThV9dfPo,1090 +pkg_resources-0.0.0.dist-info/METADATA,sha256=V9_WPOtD1FnuKrTGv6Ique7kAOn2lasvT8W0_iMCCCk,177 +pkg_resources-0.0.0.dist-info/RECORD,, +pkg_resources-0.0.0.dist-info/WHEEL,sha256=_wJFdOYk7i3xxT8ElOkUJvOdOvfNGbR9g-bf6UQT6sU,110 +pkg_resources/__init__.py,sha256=dBxLHQMU0U5ebfsfyq-bhf1TtXXNI1YIEA30mLVthjA,106677 +pkg_resources/__pycache__/__init__.cpython-37.pyc,, +pkg_resources/__pycache__/py31compat.cpython-37.pyc,, +pkg_resources/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pkg_resources/_vendor/__pycache__/__init__.cpython-37.pyc,, +pkg_resources/_vendor/__pycache__/appdirs.cpython-37.pyc,, +pkg_resources/_vendor/__pycache__/pyparsing.cpython-37.pyc,, +pkg_resources/_vendor/__pycache__/six.cpython-37.pyc,, +pkg_resources/_vendor/appdirs.py,sha256=MievUEuv3l_mQISH5SF0shDk_BNhHHzYiAPrT3ITN4I,24701 +pkg_resources/_vendor/packaging/__about__.py,sha256=zkcCPTN_6TcLW0Nrlg0176-R1QQ_WVPTm8sz1R4-HjM,720 +pkg_resources/_vendor/packaging/__init__.py,sha256=_vNac5TrzwsrzbOFIbF-5cHqc_Y2aPT2D7zrIR06BOo,513 +pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-37.pyc,, +pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-37.pyc,, +pkg_resources/_vendor/packaging/__pycache__/_compat.cpython-37.pyc,, +pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-37.pyc,, +pkg_resources/_vendor/packaging/__pycache__/markers.cpython-37.pyc,, +pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-37.pyc,, +pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-37.pyc,, +pkg_resources/_vendor/packaging/__pycache__/utils.cpython-37.pyc,, +pkg_resources/_vendor/packaging/__pycache__/version.cpython-37.pyc,, +pkg_resources/_vendor/packaging/_compat.py,sha256=Vi_A0rAQeHbU-a9X0tt1yQm9RqkgQbDSxzRw8WlU9kA,860 +pkg_resources/_vendor/packaging/_structures.py,sha256=RImECJ4c_wTlaTYYwZYLHEiebDMaAJmK1oPARhw1T5o,1416 +pkg_resources/_vendor/packaging/markers.py,sha256=uEcBBtGvzqltgnArqb9c4RrcInXezDLos14zbBHhWJo,8248 +pkg_resources/_vendor/packaging/requirements.py,sha256=SikL2UynbsT0qtY9ltqngndha_sfo0w6XGFhAhoSoaQ,4355 +pkg_resources/_vendor/packaging/specifiers.py,sha256=SAMRerzO3fK2IkFZCaZkuwZaL_EGqHNOz4pni4vhnN0,28025 +pkg_resources/_vendor/packaging/utils.py,sha256=3m6WvPm6NNxE8rkTGmn0r75B_GZSGg7ikafxHsBN1WA,421 +pkg_resources/_vendor/packaging/version.py,sha256=OwGnxYfr2ghNzYx59qWIBkrK3SnB6n-Zfd1XaLpnnM0,11556 +pkg_resources/_vendor/pyparsing.py,sha256=tmrp-lu-qO1i75ZzIN5A12nKRRD1Cm4Vpk-5LR9rims,232055 +pkg_resources/_vendor/six.py,sha256=A6hdJZVjI3t_geebZ9BzUvwRrIXo0lfwzQlM2LcKyas,30098 +pkg_resources/extern/__init__.py,sha256=cHiEfHuLmm6rs5Ve_ztBfMI7Lr31vss-D4wkqF5xzlI,2498 +pkg_resources/extern/__pycache__/__init__.cpython-37.pyc,, +pkg_resources/py31compat.py,sha256=-WQ0e4c3RG_acdhwC3gLiXhP_lg4G5q7XYkZkQg0gxU,558 diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/WHEEL b/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/WHEEL new file mode 100644 index 0000000..c4bde30 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources-0.0.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.32.3) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources/__init__.py b/Display/.venv/lib/python3.7/site-packages/pkg_resources/__init__.py new file mode 100644 index 0000000..dcfa1d0 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources/__init__.py @@ -0,0 +1,3236 @@ +# coding: utf-8 +""" +Package resource API +-------------------- + +A resource is a logical file contained within a package, or a logical +subdirectory thereof. The package resource API expects resource names +to have their path parts separated with ``/``, *not* whatever the local +path separator is. Do not use os.path operations to manipulate resource +names being passed into the API. + +The package resource API is designed to work with normal filesystem packages, +.egg files, and unpacked .egg files. It can also work in a limited way with +.zip files and with custom PEP 302 loaders that support the ``get_data()`` +method. +""" + +from __future__ import absolute_import + +import sys +import os +import io +import time +import re +import types +import zipfile +import zipimport +import warnings +import stat +import functools +import pkgutil +import operator +import platform +import collections +import plistlib +import email.parser +import errno +import tempfile +import textwrap +import itertools +import inspect +import ntpath +import posixpath +from pkgutil import get_importer + +try: + import _imp +except ImportError: + # Python 3.2 compatibility + import imp as _imp + +try: + FileExistsError +except NameError: + FileExistsError = OSError + +from pkg_resources.extern import six +from pkg_resources.extern.six.moves import urllib, map, filter + +# capture these to bypass sandboxing +from os import utime +try: + from os import mkdir, rename, unlink + WRITE_SUPPORT = True +except ImportError: + # no write support, probably under GAE + WRITE_SUPPORT = False + +from os import open as os_open +from os.path import isdir, split + +try: + import importlib.machinery as importlib_machinery + # access attribute to force import under delayed import mechanisms. + importlib_machinery.__name__ +except ImportError: + importlib_machinery = None + +from . import py31compat +from pkg_resources.extern import appdirs +from pkg_resources.extern import packaging +__import__('pkg_resources.extern.packaging.version') +__import__('pkg_resources.extern.packaging.specifiers') +__import__('pkg_resources.extern.packaging.requirements') +__import__('pkg_resources.extern.packaging.markers') + + +__metaclass__ = type + + +if (3, 0) < sys.version_info < (3, 4): + raise RuntimeError("Python 3.4 or later is required") + +if six.PY2: + # Those builtin exceptions are only defined in Python 3 + PermissionError = None + NotADirectoryError = None + +# declare some globals that will be defined later to +# satisfy the linters. +require = None +working_set = None +add_activation_listener = None +resources_stream = None +cleanup_resources = None +resource_dir = None +resource_stream = None +set_extraction_path = None +resource_isdir = None +resource_string = None +iter_entry_points = None +resource_listdir = None +resource_filename = None +resource_exists = None +_distribution_finders = None +_namespace_handlers = None +_namespace_packages = None + + +class PEP440Warning(RuntimeWarning): + """ + Used when there is an issue with a version or specifier not complying with + PEP 440. + """ + + +def parse_version(v): + try: + return packaging.version.Version(v) + except packaging.version.InvalidVersion: + return packaging.version.LegacyVersion(v) + + +_state_vars = {} + + +def _declare_state(vartype, **kw): + globals().update(kw) + _state_vars.update(dict.fromkeys(kw, vartype)) + + +def __getstate__(): + state = {} + g = globals() + for k, v in _state_vars.items(): + state[k] = g['_sget_' + v](g[k]) + return state + + +def __setstate__(state): + g = globals() + for k, v in state.items(): + g['_sset_' + _state_vars[k]](k, g[k], v) + return state + + +def _sget_dict(val): + return val.copy() + + +def _sset_dict(key, ob, state): + ob.clear() + ob.update(state) + + +def _sget_object(val): + return val.__getstate__() + + +def _sset_object(key, ob, state): + ob.__setstate__(state) + + +_sget_none = _sset_none = lambda *args: None + + +def get_supported_platform(): + """Return this platform's maximum compatible version. + + distutils.util.get_platform() normally reports the minimum version + of Mac OS X that would be required to *use* extensions produced by + distutils. But what we want when checking compatibility is to know the + version of Mac OS X that we are *running*. To allow usage of packages that + explicitly require a newer version of Mac OS X, we must also know the + current version of the OS. + + If this condition occurs for any other platform with a version in its + platform strings, this function should be extended accordingly. + """ + plat = get_build_platform() + m = macosVersionString.match(plat) + if m is not None and sys.platform == "darwin": + try: + plat = 'macosx-%s-%s' % ('.'.join(_macosx_vers()[:2]), m.group(3)) + except ValueError: + # not Mac OS X + pass + return plat + + +__all__ = [ + # Basic resource access and distribution/entry point discovery + 'require', 'run_script', 'get_provider', 'get_distribution', + 'load_entry_point', 'get_entry_map', 'get_entry_info', + 'iter_entry_points', + 'resource_string', 'resource_stream', 'resource_filename', + 'resource_listdir', 'resource_exists', 'resource_isdir', + + # Environmental control + 'declare_namespace', 'working_set', 'add_activation_listener', + 'find_distributions', 'set_extraction_path', 'cleanup_resources', + 'get_default_cache', + + # Primary implementation classes + 'Environment', 'WorkingSet', 'ResourceManager', + 'Distribution', 'Requirement', 'EntryPoint', + + # Exceptions + 'ResolutionError', 'VersionConflict', 'DistributionNotFound', + 'UnknownExtra', 'ExtractionError', + + # Warnings + 'PEP440Warning', + + # Parsing functions and string utilities + 'parse_requirements', 'parse_version', 'safe_name', 'safe_version', + 'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections', + 'safe_extra', 'to_filename', 'invalid_marker', 'evaluate_marker', + + # filesystem utilities + 'ensure_directory', 'normalize_path', + + # Distribution "precedence" constants + 'EGG_DIST', 'BINARY_DIST', 'SOURCE_DIST', 'CHECKOUT_DIST', 'DEVELOP_DIST', + + # "Provider" interfaces, implementations, and registration/lookup APIs + 'IMetadataProvider', 'IResourceProvider', 'FileMetadata', + 'PathMetadata', 'EggMetadata', 'EmptyProvider', 'empty_provider', + 'NullProvider', 'EggProvider', 'DefaultProvider', 'ZipProvider', + 'register_finder', 'register_namespace_handler', 'register_loader_type', + 'fixup_namespace_packages', 'get_importer', + + # Warnings + 'PkgResourcesDeprecationWarning', + + # Deprecated/backward compatibility only + 'run_main', 'AvailableDistributions', +] + + +class ResolutionError(Exception): + """Abstract base for dependency resolution errors""" + + def __repr__(self): + return self.__class__.__name__ + repr(self.args) + + +class VersionConflict(ResolutionError): + """ + An already-installed version conflicts with the requested version. + + Should be initialized with the installed Distribution and the requested + Requirement. + """ + + _template = "{self.dist} is installed but {self.req} is required" + + @property + def dist(self): + return self.args[0] + + @property + def req(self): + return self.args[1] + + def report(self): + return self._template.format(**locals()) + + def with_context(self, required_by): + """ + If required_by is non-empty, return a version of self that is a + ContextualVersionConflict. + """ + if not required_by: + return self + args = self.args + (required_by,) + return ContextualVersionConflict(*args) + + +class ContextualVersionConflict(VersionConflict): + """ + A VersionConflict that accepts a third parameter, the set of the + requirements that required the installed Distribution. + """ + + _template = VersionConflict._template + ' by {self.required_by}' + + @property + def required_by(self): + return self.args[2] + + +class DistributionNotFound(ResolutionError): + """A requested distribution was not found""" + + _template = ("The '{self.req}' distribution was not found " + "and is required by {self.requirers_str}") + + @property + def req(self): + return self.args[0] + + @property + def requirers(self): + return self.args[1] + + @property + def requirers_str(self): + if not self.requirers: + return 'the application' + return ', '.join(self.requirers) + + def report(self): + return self._template.format(**locals()) + + def __str__(self): + return self.report() + + +class UnknownExtra(ResolutionError): + """Distribution doesn't have an "extra feature" of the given name""" + + +_provider_factories = {} + +PY_MAJOR = sys.version[:3] +EGG_DIST = 3 +BINARY_DIST = 2 +SOURCE_DIST = 1 +CHECKOUT_DIST = 0 +DEVELOP_DIST = -1 + + +def register_loader_type(loader_type, provider_factory): + """Register `provider_factory` to make providers for `loader_type` + + `loader_type` is the type or class of a PEP 302 ``module.__loader__``, + and `provider_factory` is a function that, passed a *module* object, + returns an ``IResourceProvider`` for that module. + """ + _provider_factories[loader_type] = provider_factory + + +def get_provider(moduleOrReq): + """Return an IResourceProvider for the named module or requirement""" + if isinstance(moduleOrReq, Requirement): + return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0] + try: + module = sys.modules[moduleOrReq] + except KeyError: + __import__(moduleOrReq) + module = sys.modules[moduleOrReq] + loader = getattr(module, '__loader__', None) + return _find_adapter(_provider_factories, loader)(module) + + +def _macosx_vers(_cache=[]): + if not _cache: + version = platform.mac_ver()[0] + # fallback for MacPorts + if version == '': + plist = '/System/Library/CoreServices/SystemVersion.plist' + if os.path.exists(plist): + if hasattr(plistlib, 'readPlist'): + plist_content = plistlib.readPlist(plist) + if 'ProductVersion' in plist_content: + version = plist_content['ProductVersion'] + + _cache.append(version.split('.')) + return _cache[0] + + +def _macosx_arch(machine): + return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine) + + +def get_build_platform(): + """Return this platform's string for platform-specific distributions + + XXX Currently this is the same as ``distutils.util.get_platform()``, but it + needs some hacks for Linux and Mac OS X. + """ + from sysconfig import get_platform + + plat = get_platform() + if sys.platform == "darwin" and not plat.startswith('macosx-'): + try: + version = _macosx_vers() + machine = os.uname()[4].replace(" ", "_") + return "macosx-%d.%d-%s" % ( + int(version[0]), int(version[1]), + _macosx_arch(machine), + ) + except ValueError: + # if someone is running a non-Mac darwin system, this will fall + # through to the default implementation + pass + return plat + + +macosVersionString = re.compile(r"macosx-(\d+)\.(\d+)-(.*)") +darwinVersionString = re.compile(r"darwin-(\d+)\.(\d+)\.(\d+)-(.*)") +# XXX backward compat +get_platform = get_build_platform + + +def compatible_platforms(provided, required): + """Can code for the `provided` platform run on the `required` platform? + + Returns true if either platform is ``None``, or the platforms are equal. + + XXX Needs compatibility checks for Linux and other unixy OSes. + """ + if provided is None or required is None or provided == required: + # easy case + return True + + # Mac OS X special cases + reqMac = macosVersionString.match(required) + if reqMac: + provMac = macosVersionString.match(provided) + + # is this a Mac package? + if not provMac: + # this is backwards compatibility for packages built before + # setuptools 0.6. All packages built after this point will + # use the new macosx designation. + provDarwin = darwinVersionString.match(provided) + if provDarwin: + dversion = int(provDarwin.group(1)) + macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2)) + if dversion == 7 and macosversion >= "10.3" or \ + dversion == 8 and macosversion >= "10.4": + return True + # egg isn't macosx or legacy darwin + return False + + # are they the same major version and machine type? + if provMac.group(1) != reqMac.group(1) or \ + provMac.group(3) != reqMac.group(3): + return False + + # is the required OS major update >= the provided one? + if int(provMac.group(2)) > int(reqMac.group(2)): + return False + + return True + + # XXX Linux and other platforms' special cases should go here + return False + + +def run_script(dist_spec, script_name): + """Locate distribution `dist_spec` and run its `script_name` script""" + ns = sys._getframe(1).f_globals + name = ns['__name__'] + ns.clear() + ns['__name__'] = name + require(dist_spec)[0].run_script(script_name, ns) + + +# backward compatibility +run_main = run_script + + +def get_distribution(dist): + """Return a current distribution object for a Requirement or string""" + if isinstance(dist, six.string_types): + dist = Requirement.parse(dist) + if isinstance(dist, Requirement): + dist = get_provider(dist) + if not isinstance(dist, Distribution): + raise TypeError("Expected string, Requirement, or Distribution", dist) + return dist + + +def load_entry_point(dist, group, name): + """Return `name` entry point of `group` for `dist` or raise ImportError""" + return get_distribution(dist).load_entry_point(group, name) + + +def get_entry_map(dist, group=None): + """Return the entry point map for `group`, or the full entry map""" + return get_distribution(dist).get_entry_map(group) + + +def get_entry_info(dist, group, name): + """Return the EntryPoint object for `group`+`name`, or ``None``""" + return get_distribution(dist).get_entry_info(group, name) + + +class IMetadataProvider: + def has_metadata(name): + """Does the package's distribution contain the named metadata?""" + + def get_metadata(name): + """The named metadata resource as a string""" + + def get_metadata_lines(name): + """Yield named metadata resource as list of non-blank non-comment lines + + Leading and trailing whitespace is stripped from each line, and lines + with ``#`` as the first non-blank character are omitted.""" + + def metadata_isdir(name): + """Is the named metadata a directory? (like ``os.path.isdir()``)""" + + def metadata_listdir(name): + """List of metadata names in the directory (like ``os.listdir()``)""" + + def run_script(script_name, namespace): + """Execute the named script in the supplied namespace dictionary""" + + +class IResourceProvider(IMetadataProvider): + """An object that provides access to package resources""" + + def get_resource_filename(manager, resource_name): + """Return a true filesystem path for `resource_name` + + `manager` must be an ``IResourceManager``""" + + def get_resource_stream(manager, resource_name): + """Return a readable file-like object for `resource_name` + + `manager` must be an ``IResourceManager``""" + + def get_resource_string(manager, resource_name): + """Return a string containing the contents of `resource_name` + + `manager` must be an ``IResourceManager``""" + + def has_resource(resource_name): + """Does the package contain the named resource?""" + + def resource_isdir(resource_name): + """Is the named resource a directory? (like ``os.path.isdir()``)""" + + def resource_listdir(resource_name): + """List of resource names in the directory (like ``os.listdir()``)""" + + +class WorkingSet: + """A collection of active distributions on sys.path (or a similar list)""" + + def __init__(self, entries=None): + """Create working set from list of path entries (default=sys.path)""" + self.entries = [] + self.entry_keys = {} + self.by_key = {} + self.callbacks = [] + + if entries is None: + entries = sys.path + + for entry in entries: + self.add_entry(entry) + + @classmethod + def _build_master(cls): + """ + Prepare the master working set. + """ + ws = cls() + try: + from __main__ import __requires__ + except ImportError: + # The main program does not list any requirements + return ws + + # ensure the requirements are met + try: + ws.require(__requires__) + except VersionConflict: + return cls._build_from_requirements(__requires__) + + return ws + + @classmethod + def _build_from_requirements(cls, req_spec): + """ + Build a working set from a requirement spec. Rewrites sys.path. + """ + # try it without defaults already on sys.path + # by starting with an empty path + ws = cls([]) + reqs = parse_requirements(req_spec) + dists = ws.resolve(reqs, Environment()) + for dist in dists: + ws.add(dist) + + # add any missing entries from sys.path + for entry in sys.path: + if entry not in ws.entries: + ws.add_entry(entry) + + # then copy back to sys.path + sys.path[:] = ws.entries + return ws + + def add_entry(self, entry): + """Add a path item to ``.entries``, finding any distributions on it + + ``find_distributions(entry, True)`` is used to find distributions + corresponding to the path entry, and they are added. `entry` is + always appended to ``.entries``, even if it is already present. + (This is because ``sys.path`` can contain the same value more than + once, and the ``.entries`` of the ``sys.path`` WorkingSet should always + equal ``sys.path``.) + """ + self.entry_keys.setdefault(entry, []) + self.entries.append(entry) + for dist in find_distributions(entry, True): + self.add(dist, entry, False) + + def __contains__(self, dist): + """True if `dist` is the active distribution for its project""" + return self.by_key.get(dist.key) == dist + + def find(self, req): + """Find a distribution matching requirement `req` + + If there is an active distribution for the requested project, this + returns it as long as it meets the version requirement specified by + `req`. But, if there is an active distribution for the project and it + does *not* meet the `req` requirement, ``VersionConflict`` is raised. + If there is no active distribution for the requested project, ``None`` + is returned. + """ + dist = self.by_key.get(req.key) + if dist is not None and dist not in req: + # XXX add more info + raise VersionConflict(dist, req) + return dist + + def iter_entry_points(self, group, name=None): + """Yield entry point objects from `group` matching `name` + + If `name` is None, yields all entry points in `group` from all + distributions in the working set, otherwise only ones matching + both `group` and `name` are yielded (in distribution order). + """ + return ( + entry + for dist in self + for entry in dist.get_entry_map(group).values() + if name is None or name == entry.name + ) + + def run_script(self, requires, script_name): + """Locate distribution for `requires` and run `script_name` script""" + ns = sys._getframe(1).f_globals + name = ns['__name__'] + ns.clear() + ns['__name__'] = name + self.require(requires)[0].run_script(script_name, ns) + + def __iter__(self): + """Yield distributions for non-duplicate projects in the working set + + The yield order is the order in which the items' path entries were + added to the working set. + """ + seen = {} + for item in self.entries: + if item not in self.entry_keys: + # workaround a cache issue + continue + + for key in self.entry_keys[item]: + if key not in seen: + seen[key] = 1 + yield self.by_key[key] + + def add(self, dist, entry=None, insert=True, replace=False): + """Add `dist` to working set, associated with `entry` + + If `entry` is unspecified, it defaults to the ``.location`` of `dist`. + On exit from this routine, `entry` is added to the end of the working + set's ``.entries`` (if it wasn't already present). + + `dist` is only added to the working set if it's for a project that + doesn't already have a distribution in the set, unless `replace=True`. + If it's added, any callbacks registered with the ``subscribe()`` method + will be called. + """ + if insert: + dist.insert_on(self.entries, entry, replace=replace) + + if entry is None: + entry = dist.location + keys = self.entry_keys.setdefault(entry, []) + keys2 = self.entry_keys.setdefault(dist.location, []) + if not replace and dist.key in self.by_key: + # ignore hidden distros + return + + self.by_key[dist.key] = dist + if dist.key not in keys: + keys.append(dist.key) + if dist.key not in keys2: + keys2.append(dist.key) + self._added_new(dist) + + def resolve(self, requirements, env=None, installer=None, + replace_conflicting=False, extras=None): + """List all distributions needed to (recursively) meet `requirements` + + `requirements` must be a sequence of ``Requirement`` objects. `env`, + if supplied, should be an ``Environment`` instance. If + not supplied, it defaults to all distributions available within any + entry or distribution in the working set. `installer`, if supplied, + will be invoked with each requirement that cannot be met by an + already-installed distribution; it should return a ``Distribution`` or + ``None``. + + Unless `replace_conflicting=True`, raises a VersionConflict exception + if + any requirements are found on the path that have the correct name but + the wrong version. Otherwise, if an `installer` is supplied it will be + invoked to obtain the correct version of the requirement and activate + it. + + `extras` is a list of the extras to be used with these requirements. + This is important because extra requirements may look like `my_req; + extra = "my_extra"`, which would otherwise be interpreted as a purely + optional requirement. Instead, we want to be able to assert that these + requirements are truly required. + """ + + # set up the stack + requirements = list(requirements)[::-1] + # set of processed requirements + processed = {} + # key -> dist + best = {} + to_activate = [] + + req_extras = _ReqExtras() + + # Mapping of requirement to set of distributions that required it; + # useful for reporting info about conflicts. + required_by = collections.defaultdict(set) + + while requirements: + # process dependencies breadth-first + req = requirements.pop(0) + if req in processed: + # Ignore cyclic or redundant dependencies + continue + + if not req_extras.markers_pass(req, extras): + continue + + dist = best.get(req.key) + if dist is None: + # Find the best distribution and add it to the map + dist = self.by_key.get(req.key) + if dist is None or (dist not in req and replace_conflicting): + ws = self + if env is None: + if dist is None: + env = Environment(self.entries) + else: + # Use an empty environment and workingset to avoid + # any further conflicts with the conflicting + # distribution + env = Environment([]) + ws = WorkingSet([]) + dist = best[req.key] = env.best_match( + req, ws, installer, + replace_conflicting=replace_conflicting + ) + if dist is None: + requirers = required_by.get(req, None) + raise DistributionNotFound(req, requirers) + to_activate.append(dist) + if dist not in req: + # Oops, the "best" so far conflicts with a dependency + dependent_req = required_by[req] + raise VersionConflict(dist, req).with_context(dependent_req) + + # push the new requirements onto the stack + new_requirements = dist.requires(req.extras)[::-1] + requirements.extend(new_requirements) + + # Register the new requirements needed by req + for new_requirement in new_requirements: + required_by[new_requirement].add(req.project_name) + req_extras[new_requirement] = req.extras + + processed[req] = True + + # return list of distros to activate + return to_activate + + def find_plugins( + self, plugin_env, full_env=None, installer=None, fallback=True): + """Find all activatable distributions in `plugin_env` + + Example usage:: + + distributions, errors = working_set.find_plugins( + Environment(plugin_dirlist) + ) + # add plugins+libs to sys.path + map(working_set.add, distributions) + # display errors + print('Could not load', errors) + + The `plugin_env` should be an ``Environment`` instance that contains + only distributions that are in the project's "plugin directory" or + directories. The `full_env`, if supplied, should be an ``Environment`` + contains all currently-available distributions. If `full_env` is not + supplied, one is created automatically from the ``WorkingSet`` this + method is called on, which will typically mean that every directory on + ``sys.path`` will be scanned for distributions. + + `installer` is a standard installer callback as used by the + ``resolve()`` method. The `fallback` flag indicates whether we should + attempt to resolve older versions of a plugin if the newest version + cannot be resolved. + + This method returns a 2-tuple: (`distributions`, `error_info`), where + `distributions` is a list of the distributions found in `plugin_env` + that were loadable, along with any other distributions that are needed + to resolve their dependencies. `error_info` is a dictionary mapping + unloadable plugin distributions to an exception instance describing the + error that occurred. Usually this will be a ``DistributionNotFound`` or + ``VersionConflict`` instance. + """ + + plugin_projects = list(plugin_env) + # scan project names in alphabetic order + plugin_projects.sort() + + error_info = {} + distributions = {} + + if full_env is None: + env = Environment(self.entries) + env += plugin_env + else: + env = full_env + plugin_env + + shadow_set = self.__class__([]) + # put all our entries in shadow_set + list(map(shadow_set.add, self)) + + for project_name in plugin_projects: + + for dist in plugin_env[project_name]: + + req = [dist.as_requirement()] + + try: + resolvees = shadow_set.resolve(req, env, installer) + + except ResolutionError as v: + # save error info + error_info[dist] = v + if fallback: + # try the next older version of project + continue + else: + # give up on this project, keep going + break + + else: + list(map(shadow_set.add, resolvees)) + distributions.update(dict.fromkeys(resolvees)) + + # success, no need to try any more versions of this project + break + + distributions = list(distributions) + distributions.sort() + + return distributions, error_info + + def require(self, *requirements): + """Ensure that distributions matching `requirements` are activated + + `requirements` must be a string or a (possibly-nested) sequence + thereof, specifying the distributions and versions required. The + return value is a sequence of the distributions that needed to be + activated to fulfill the requirements; all relevant distributions are + included, even if they were already activated in this working set. + """ + needed = self.resolve(parse_requirements(requirements)) + + for dist in needed: + self.add(dist) + + return needed + + def subscribe(self, callback, existing=True): + """Invoke `callback` for all distributions + + If `existing=True` (default), + call on all existing ones, as well. + """ + if callback in self.callbacks: + return + self.callbacks.append(callback) + if not existing: + return + for dist in self: + callback(dist) + + def _added_new(self, dist): + for callback in self.callbacks: + callback(dist) + + def __getstate__(self): + return ( + self.entries[:], self.entry_keys.copy(), self.by_key.copy(), + self.callbacks[:] + ) + + def __setstate__(self, e_k_b_c): + entries, keys, by_key, callbacks = e_k_b_c + self.entries = entries[:] + self.entry_keys = keys.copy() + self.by_key = by_key.copy() + self.callbacks = callbacks[:] + + +class _ReqExtras(dict): + """ + Map each requirement to the extras that demanded it. + """ + + def markers_pass(self, req, extras=None): + """ + Evaluate markers for req against each extra that + demanded it. + + Return False if the req has a marker and fails + evaluation. Otherwise, return True. + """ + extra_evals = ( + req.marker.evaluate({'extra': extra}) + for extra in self.get(req, ()) + (extras or (None,)) + ) + return not req.marker or any(extra_evals) + + +class Environment: + """Searchable snapshot of distributions on a search path""" + + def __init__( + self, search_path=None, platform=get_supported_platform(), + python=PY_MAJOR): + """Snapshot distributions available on a search path + + Any distributions found on `search_path` are added to the environment. + `search_path` should be a sequence of ``sys.path`` items. If not + supplied, ``sys.path`` is used. + + `platform` is an optional string specifying the name of the platform + that platform-specific distributions must be compatible with. If + unspecified, it defaults to the current platform. `python` is an + optional string naming the desired version of Python (e.g. ``'3.6'``); + it defaults to the current version. + + You may explicitly set `platform` (and/or `python`) to ``None`` if you + wish to map *all* distributions, not just those compatible with the + running platform or Python version. + """ + self._distmap = {} + self.platform = platform + self.python = python + self.scan(search_path) + + def can_add(self, dist): + """Is distribution `dist` acceptable for this environment? + + The distribution must match the platform and python version + requirements specified when this environment was created, or False + is returned. + """ + py_compat = ( + self.python is None + or dist.py_version is None + or dist.py_version == self.python + ) + return py_compat and compatible_platforms(dist.platform, self.platform) + + def remove(self, dist): + """Remove `dist` from the environment""" + self._distmap[dist.key].remove(dist) + + def scan(self, search_path=None): + """Scan `search_path` for distributions usable in this environment + + Any distributions found are added to the environment. + `search_path` should be a sequence of ``sys.path`` items. If not + supplied, ``sys.path`` is used. Only distributions conforming to + the platform/python version defined at initialization are added. + """ + if search_path is None: + search_path = sys.path + + for item in search_path: + for dist in find_distributions(item): + self.add(dist) + + def __getitem__(self, project_name): + """Return a newest-to-oldest list of distributions for `project_name` + + Uses case-insensitive `project_name` comparison, assuming all the + project's distributions use their project's name converted to all + lowercase as their key. + + """ + distribution_key = project_name.lower() + return self._distmap.get(distribution_key, []) + + def add(self, dist): + """Add `dist` if we ``can_add()`` it and it has not already been added + """ + if self.can_add(dist) and dist.has_version(): + dists = self._distmap.setdefault(dist.key, []) + if dist not in dists: + dists.append(dist) + dists.sort(key=operator.attrgetter('hashcmp'), reverse=True) + + def best_match( + self, req, working_set, installer=None, replace_conflicting=False): + """Find distribution best matching `req` and usable on `working_set` + + This calls the ``find(req)`` method of the `working_set` to see if a + suitable distribution is already active. (This may raise + ``VersionConflict`` if an unsuitable version of the project is already + active in the specified `working_set`.) If a suitable distribution + isn't active, this method returns the newest distribution in the + environment that meets the ``Requirement`` in `req`. If no suitable + distribution is found, and `installer` is supplied, then the result of + calling the environment's ``obtain(req, installer)`` method will be + returned. + """ + try: + dist = working_set.find(req) + except VersionConflict: + if not replace_conflicting: + raise + dist = None + if dist is not None: + return dist + for dist in self[req.key]: + if dist in req: + return dist + # try to download/install + return self.obtain(req, installer) + + def obtain(self, requirement, installer=None): + """Obtain a distribution matching `requirement` (e.g. via download) + + Obtain a distro that matches requirement (e.g. via download). In the + base ``Environment`` class, this routine just returns + ``installer(requirement)``, unless `installer` is None, in which case + None is returned instead. This method is a hook that allows subclasses + to attempt other ways of obtaining a distribution before falling back + to the `installer` argument.""" + if installer is not None: + return installer(requirement) + + def __iter__(self): + """Yield the unique project names of the available distributions""" + for key in self._distmap.keys(): + if self[key]: + yield key + + def __iadd__(self, other): + """In-place addition of a distribution or environment""" + if isinstance(other, Distribution): + self.add(other) + elif isinstance(other, Environment): + for project in other: + for dist in other[project]: + self.add(dist) + else: + raise TypeError("Can't add %r to environment" % (other,)) + return self + + def __add__(self, other): + """Add an environment or distribution to an environment""" + new = self.__class__([], platform=None, python=None) + for env in self, other: + new += env + return new + + +# XXX backward compatibility +AvailableDistributions = Environment + + +class ExtractionError(RuntimeError): + """An error occurred extracting a resource + + The following attributes are available from instances of this exception: + + manager + The resource manager that raised this exception + + cache_path + The base directory for resource extraction + + original_error + The exception instance that caused extraction to fail + """ + + +class ResourceManager: + """Manage resource extraction and packages""" + extraction_path = None + + def __init__(self): + self.cached_files = {} + + def resource_exists(self, package_or_requirement, resource_name): + """Does the named resource exist?""" + return get_provider(package_or_requirement).has_resource(resource_name) + + def resource_isdir(self, package_or_requirement, resource_name): + """Is the named resource an existing directory?""" + return get_provider(package_or_requirement).resource_isdir( + resource_name + ) + + def resource_filename(self, package_or_requirement, resource_name): + """Return a true filesystem path for specified resource""" + return get_provider(package_or_requirement).get_resource_filename( + self, resource_name + ) + + def resource_stream(self, package_or_requirement, resource_name): + """Return a readable file-like object for specified resource""" + return get_provider(package_or_requirement).get_resource_stream( + self, resource_name + ) + + def resource_string(self, package_or_requirement, resource_name): + """Return specified resource as a string""" + return get_provider(package_or_requirement).get_resource_string( + self, resource_name + ) + + def resource_listdir(self, package_or_requirement, resource_name): + """List the contents of the named resource directory""" + return get_provider(package_or_requirement).resource_listdir( + resource_name + ) + + def extraction_error(self): + """Give an error message for problems extracting file(s)""" + + old_exc = sys.exc_info()[1] + cache_path = self.extraction_path or get_default_cache() + + tmpl = textwrap.dedent(""" + Can't extract file(s) to egg cache + + The following error occurred while trying to extract file(s) + to the Python egg cache: + + {old_exc} + + The Python egg cache directory is currently set to: + + {cache_path} + + Perhaps your account does not have write access to this directory? + You can change the cache directory by setting the PYTHON_EGG_CACHE + environment variable to point to an accessible directory. + """).lstrip() + err = ExtractionError(tmpl.format(**locals())) + err.manager = self + err.cache_path = cache_path + err.original_error = old_exc + raise err + + def get_cache_path(self, archive_name, names=()): + """Return absolute location in cache for `archive_name` and `names` + + The parent directory of the resulting path will be created if it does + not already exist. `archive_name` should be the base filename of the + enclosing egg (which may not be the name of the enclosing zipfile!), + including its ".egg" extension. `names`, if provided, should be a + sequence of path name parts "under" the egg's extraction location. + + This method should only be called by resource providers that need to + obtain an extraction location, and only for names they intend to + extract, as it tracks the generated names for possible cleanup later. + """ + extract_path = self.extraction_path or get_default_cache() + target_path = os.path.join(extract_path, archive_name + '-tmp', *names) + try: + _bypass_ensure_directory(target_path) + except Exception: + self.extraction_error() + + self._warn_unsafe_extraction_path(extract_path) + + self.cached_files[target_path] = 1 + return target_path + + @staticmethod + def _warn_unsafe_extraction_path(path): + """ + If the default extraction path is overridden and set to an insecure + location, such as /tmp, it opens up an opportunity for an attacker to + replace an extracted file with an unauthorized payload. Warn the user + if a known insecure location is used. + + See Distribute #375 for more details. + """ + if os.name == 'nt' and not path.startswith(os.environ['windir']): + # On Windows, permissions are generally restrictive by default + # and temp directories are not writable by other users, so + # bypass the warning. + return + mode = os.stat(path).st_mode + if mode & stat.S_IWOTH or mode & stat.S_IWGRP: + msg = ( + "%s is writable by group/others and vulnerable to attack " + "when " + "used with get_resource_filename. Consider a more secure " + "location (set with .set_extraction_path or the " + "PYTHON_EGG_CACHE environment variable)." % path + ) + warnings.warn(msg, UserWarning) + + def postprocess(self, tempname, filename): + """Perform any platform-specific postprocessing of `tempname` + + This is where Mac header rewrites should be done; other platforms don't + have anything special they should do. + + Resource providers should call this method ONLY after successfully + extracting a compressed resource. They must NOT call it on resources + that are already in the filesystem. + + `tempname` is the current (temporary) name of the file, and `filename` + is the name it will be renamed to by the caller after this routine + returns. + """ + + if os.name == 'posix': + # Make the resource executable + mode = ((os.stat(tempname).st_mode) | 0o555) & 0o7777 + os.chmod(tempname, mode) + + def set_extraction_path(self, path): + """Set the base path where resources will be extracted to, if needed. + + If you do not call this routine before any extractions take place, the + path defaults to the return value of ``get_default_cache()``. (Which + is based on the ``PYTHON_EGG_CACHE`` environment variable, with various + platform-specific fallbacks. See that routine's documentation for more + details.) + + Resources are extracted to subdirectories of this path based upon + information given by the ``IResourceProvider``. You may set this to a + temporary directory, but then you must call ``cleanup_resources()`` to + delete the extracted files when done. There is no guarantee that + ``cleanup_resources()`` will be able to remove all extracted files. + + (Note: you may not change the extraction path for a given resource + manager once resources have been extracted, unless you first call + ``cleanup_resources()``.) + """ + if self.cached_files: + raise ValueError( + "Can't change extraction path, files already extracted" + ) + + self.extraction_path = path + + def cleanup_resources(self, force=False): + """ + Delete all extracted resource files and directories, returning a list + of the file and directory names that could not be successfully removed. + This function does not have any concurrency protection, so it should + generally only be called when the extraction path is a temporary + directory exclusive to a single process. This method is not + automatically called; you must call it explicitly or register it as an + ``atexit`` function if you wish to ensure cleanup of a temporary + directory used for extractions. + """ + # XXX + + +def get_default_cache(): + """ + Return the ``PYTHON_EGG_CACHE`` environment variable + or a platform-relevant user cache dir for an app + named "Python-Eggs". + """ + return ( + os.environ.get('PYTHON_EGG_CACHE') + or appdirs.user_cache_dir(appname='Python-Eggs') + ) + + +def safe_name(name): + """Convert an arbitrary string to a standard distribution name + + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + """ + return re.sub('[^A-Za-z0-9.]+', '-', name) + + +def safe_version(version): + """ + Convert an arbitrary string to a standard version string + """ + try: + # normalize the version + return str(packaging.version.Version(version)) + except packaging.version.InvalidVersion: + version = version.replace(' ', '.') + return re.sub('[^A-Za-z0-9.]+', '-', version) + + +def safe_extra(extra): + """Convert an arbitrary string to a standard 'extra' name + + Any runs of non-alphanumeric characters are replaced with a single '_', + and the result is always lowercased. + """ + return re.sub('[^A-Za-z0-9.-]+', '_', extra).lower() + + +def to_filename(name): + """Convert a project or version name to its filename-escaped form + + Any '-' characters are currently replaced with '_'. + """ + return name.replace('-', '_') + + +def invalid_marker(text): + """ + Validate text as a PEP 508 environment marker; return an exception + if invalid or False otherwise. + """ + try: + evaluate_marker(text) + except SyntaxError as e: + e.filename = None + e.lineno = None + return e + return False + + +def evaluate_marker(text, extra=None): + """ + Evaluate a PEP 508 environment marker. + Return a boolean indicating the marker result in this environment. + Raise SyntaxError if marker is invalid. + + This implementation uses the 'pyparsing' module. + """ + try: + marker = packaging.markers.Marker(text) + return marker.evaluate() + except packaging.markers.InvalidMarker as e: + raise SyntaxError(e) + + +class NullProvider: + """Try to implement resources and metadata for arbitrary PEP 302 loaders""" + + egg_name = None + egg_info = None + loader = None + + def __init__(self, module): + self.loader = getattr(module, '__loader__', None) + self.module_path = os.path.dirname(getattr(module, '__file__', '')) + + def get_resource_filename(self, manager, resource_name): + return self._fn(self.module_path, resource_name) + + def get_resource_stream(self, manager, resource_name): + return io.BytesIO(self.get_resource_string(manager, resource_name)) + + def get_resource_string(self, manager, resource_name): + return self._get(self._fn(self.module_path, resource_name)) + + def has_resource(self, resource_name): + return self._has(self._fn(self.module_path, resource_name)) + + def has_metadata(self, name): + return self.egg_info and self._has(self._fn(self.egg_info, name)) + + def get_metadata(self, name): + if not self.egg_info: + return "" + value = self._get(self._fn(self.egg_info, name)) + return value.decode('utf-8') if six.PY3 else value + + def get_metadata_lines(self, name): + return yield_lines(self.get_metadata(name)) + + def resource_isdir(self, resource_name): + return self._isdir(self._fn(self.module_path, resource_name)) + + def metadata_isdir(self, name): + return self.egg_info and self._isdir(self._fn(self.egg_info, name)) + + def resource_listdir(self, resource_name): + return self._listdir(self._fn(self.module_path, resource_name)) + + def metadata_listdir(self, name): + if self.egg_info: + return self._listdir(self._fn(self.egg_info, name)) + return [] + + def run_script(self, script_name, namespace): + script = 'scripts/' + script_name + if not self.has_metadata(script): + raise ResolutionError( + "Script {script!r} not found in metadata at {self.egg_info!r}" + .format(**locals()), + ) + script_text = self.get_metadata(script).replace('\r\n', '\n') + script_text = script_text.replace('\r', '\n') + script_filename = self._fn(self.egg_info, script) + namespace['__file__'] = script_filename + if os.path.exists(script_filename): + source = open(script_filename).read() + code = compile(source, script_filename, 'exec') + exec(code, namespace, namespace) + else: + from linecache import cache + cache[script_filename] = ( + len(script_text), 0, script_text.split('\n'), script_filename + ) + script_code = compile(script_text, script_filename, 'exec') + exec(script_code, namespace, namespace) + + def _has(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _isdir(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _listdir(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _fn(self, base, resource_name): + self._validate_resource_path(resource_name) + if resource_name: + return os.path.join(base, *resource_name.split('/')) + return base + + @staticmethod + def _validate_resource_path(path): + """ + Validate the resource paths according to the docs. + https://setuptools.readthedocs.io/en/latest/pkg_resources.html#basic-resource-access + + >>> warned = getfixture('recwarn') + >>> warnings.simplefilter('always') + >>> vrp = NullProvider._validate_resource_path + >>> vrp('foo/bar.txt') + >>> bool(warned) + False + >>> vrp('../foo/bar.txt') + >>> bool(warned) + True + >>> warned.clear() + >>> vrp('/foo/bar.txt') + >>> bool(warned) + True + >>> vrp('foo/../../bar.txt') + >>> bool(warned) + True + >>> warned.clear() + >>> vrp('foo/f../bar.txt') + >>> bool(warned) + False + + Windows path separators are straight-up disallowed. + >>> vrp(r'\\foo/bar.txt') + Traceback (most recent call last): + ... + ValueError: Use of .. or absolute path in a resource path \ +is not allowed. + + >>> vrp(r'C:\\foo/bar.txt') + Traceback (most recent call last): + ... + ValueError: Use of .. or absolute path in a resource path \ +is not allowed. + """ + invalid = ( + os.path.pardir in path.split(posixpath.sep) or + posixpath.isabs(path) or + ntpath.isabs(path) + ) + if not invalid: + return + + msg = "Use of .. or absolute path in a resource path is not allowed." + + # Aggressively disallow Windows absolute paths + if ntpath.isabs(path) and not posixpath.isabs(path): + raise ValueError(msg) + + # for compatibility, warn; in future + # raise ValueError(msg) + warnings.warn( + msg[:-1] + " and will raise exceptions in a future release.", + DeprecationWarning, + stacklevel=4, + ) + + def _get(self, path): + if hasattr(self.loader, 'get_data'): + return self.loader.get_data(path) + raise NotImplementedError( + "Can't perform this operation for loaders without 'get_data()'" + ) + + +register_loader_type(object, NullProvider) + + +class EggProvider(NullProvider): + """Provider based on a virtual filesystem""" + + def __init__(self, module): + NullProvider.__init__(self, module) + self._setup_prefix() + + def _setup_prefix(self): + # we assume here that our metadata may be nested inside a "basket" + # of multiple eggs; that's why we use module_path instead of .archive + path = self.module_path + old = None + while path != old: + if _is_egg_path(path): + self.egg_name = os.path.basename(path) + self.egg_info = os.path.join(path, 'EGG-INFO') + self.egg_root = path + break + old = path + path, base = os.path.split(path) + + +class DefaultProvider(EggProvider): + """Provides access to package resources in the filesystem""" + + def _has(self, path): + return os.path.exists(path) + + def _isdir(self, path): + return os.path.isdir(path) + + def _listdir(self, path): + return os.listdir(path) + + def get_resource_stream(self, manager, resource_name): + return open(self._fn(self.module_path, resource_name), 'rb') + + def _get(self, path): + with open(path, 'rb') as stream: + return stream.read() + + @classmethod + def _register(cls): + loader_names = 'SourceFileLoader', 'SourcelessFileLoader', + for name in loader_names: + loader_cls = getattr(importlib_machinery, name, type(None)) + register_loader_type(loader_cls, cls) + + +DefaultProvider._register() + + +class EmptyProvider(NullProvider): + """Provider that returns nothing for all requests""" + + module_path = None + + _isdir = _has = lambda self, path: False + + def _get(self, path): + return '' + + def _listdir(self, path): + return [] + + def __init__(self): + pass + + +empty_provider = EmptyProvider() + + +class ZipManifests(dict): + """ + zip manifest builder + """ + + @classmethod + def build(cls, path): + """ + Build a dictionary similar to the zipimport directory + caches, except instead of tuples, store ZipInfo objects. + + Use a platform-specific path separator (os.sep) for the path keys + for compatibility with pypy on Windows. + """ + with zipfile.ZipFile(path) as zfile: + items = ( + ( + name.replace('/', os.sep), + zfile.getinfo(name), + ) + for name in zfile.namelist() + ) + return dict(items) + + load = build + + +class MemoizedZipManifests(ZipManifests): + """ + Memoized zipfile manifests. + """ + manifest_mod = collections.namedtuple('manifest_mod', 'manifest mtime') + + def load(self, path): + """ + Load a manifest at path or return a suitable manifest already loaded. + """ + path = os.path.normpath(path) + mtime = os.stat(path).st_mtime + + if path not in self or self[path].mtime != mtime: + manifest = self.build(path) + self[path] = self.manifest_mod(manifest, mtime) + + return self[path].manifest + + +class ZipProvider(EggProvider): + """Resource support for zips and eggs""" + + eagers = None + _zip_manifests = MemoizedZipManifests() + + def __init__(self, module): + EggProvider.__init__(self, module) + self.zip_pre = self.loader.archive + os.sep + + def _zipinfo_name(self, fspath): + # Convert a virtual filename (full path to file) into a zipfile subpath + # usable with the zipimport directory cache for our target archive + fspath = fspath.rstrip(os.sep) + if fspath == self.loader.archive: + return '' + if fspath.startswith(self.zip_pre): + return fspath[len(self.zip_pre):] + raise AssertionError( + "%s is not a subpath of %s" % (fspath, self.zip_pre) + ) + + def _parts(self, zip_path): + # Convert a zipfile subpath into an egg-relative path part list. + # pseudo-fs path + fspath = self.zip_pre + zip_path + if fspath.startswith(self.egg_root + os.sep): + return fspath[len(self.egg_root) + 1:].split(os.sep) + raise AssertionError( + "%s is not a subpath of %s" % (fspath, self.egg_root) + ) + + @property + def zipinfo(self): + return self._zip_manifests.load(self.loader.archive) + + def get_resource_filename(self, manager, resource_name): + if not self.egg_name: + raise NotImplementedError( + "resource_filename() only supported for .egg, not .zip" + ) + # no need to lock for extraction, since we use temp names + zip_path = self._resource_to_zip(resource_name) + eagers = self._get_eager_resources() + if '/'.join(self._parts(zip_path)) in eagers: + for name in eagers: + self._extract_resource(manager, self._eager_to_zip(name)) + return self._extract_resource(manager, zip_path) + + @staticmethod + def _get_date_and_size(zip_stat): + size = zip_stat.file_size + # ymdhms+wday, yday, dst + date_time = zip_stat.date_time + (0, 0, -1) + # 1980 offset already done + timestamp = time.mktime(date_time) + return timestamp, size + + def _extract_resource(self, manager, zip_path): + + if zip_path in self._index(): + for name in self._index()[zip_path]: + last = self._extract_resource( + manager, os.path.join(zip_path, name) + ) + # return the extracted directory name + return os.path.dirname(last) + + timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) + + if not WRITE_SUPPORT: + raise IOError('"os.rename" and "os.unlink" are not supported ' + 'on this platform') + try: + + real_path = manager.get_cache_path( + self.egg_name, self._parts(zip_path) + ) + + if self._is_current(real_path, zip_path): + return real_path + + outf, tmpnam = _mkstemp( + ".$extract", + dir=os.path.dirname(real_path), + ) + os.write(outf, self.loader.get_data(zip_path)) + os.close(outf) + utime(tmpnam, (timestamp, timestamp)) + manager.postprocess(tmpnam, real_path) + + try: + rename(tmpnam, real_path) + + except os.error: + if os.path.isfile(real_path): + if self._is_current(real_path, zip_path): + # the file became current since it was checked above, + # so proceed. + return real_path + # Windows, del old file and retry + elif os.name == 'nt': + unlink(real_path) + rename(tmpnam, real_path) + return real_path + raise + + except os.error: + # report a user-friendly error + manager.extraction_error() + + return real_path + + def _is_current(self, file_path, zip_path): + """ + Return True if the file_path is current for this zip_path + """ + timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) + if not os.path.isfile(file_path): + return False + stat = os.stat(file_path) + if stat.st_size != size or stat.st_mtime != timestamp: + return False + # check that the contents match + zip_contents = self.loader.get_data(zip_path) + with open(file_path, 'rb') as f: + file_contents = f.read() + return zip_contents == file_contents + + def _get_eager_resources(self): + if self.eagers is None: + eagers = [] + for name in ('native_libs.txt', 'eager_resources.txt'): + if self.has_metadata(name): + eagers.extend(self.get_metadata_lines(name)) + self.eagers = eagers + return self.eagers + + def _index(self): + try: + return self._dirindex + except AttributeError: + ind = {} + for path in self.zipinfo: + parts = path.split(os.sep) + while parts: + parent = os.sep.join(parts[:-1]) + if parent in ind: + ind[parent].append(parts[-1]) + break + else: + ind[parent] = [parts.pop()] + self._dirindex = ind + return ind + + def _has(self, fspath): + zip_path = self._zipinfo_name(fspath) + return zip_path in self.zipinfo or zip_path in self._index() + + def _isdir(self, fspath): + return self._zipinfo_name(fspath) in self._index() + + def _listdir(self, fspath): + return list(self._index().get(self._zipinfo_name(fspath), ())) + + def _eager_to_zip(self, resource_name): + return self._zipinfo_name(self._fn(self.egg_root, resource_name)) + + def _resource_to_zip(self, resource_name): + return self._zipinfo_name(self._fn(self.module_path, resource_name)) + + +register_loader_type(zipimport.zipimporter, ZipProvider) + + +class FileMetadata(EmptyProvider): + """Metadata handler for standalone PKG-INFO files + + Usage:: + + metadata = FileMetadata("/path/to/PKG-INFO") + + This provider rejects all data and metadata requests except for PKG-INFO, + which is treated as existing, and will be the contents of the file at + the provided location. + """ + + def __init__(self, path): + self.path = path + + def has_metadata(self, name): + return name == 'PKG-INFO' and os.path.isfile(self.path) + + def get_metadata(self, name): + if name != 'PKG-INFO': + raise KeyError("No metadata except PKG-INFO is available") + + with io.open(self.path, encoding='utf-8', errors="replace") as f: + metadata = f.read() + self._warn_on_replacement(metadata) + return metadata + + def _warn_on_replacement(self, metadata): + # Python 2.7 compat for: replacement_char = '�' + replacement_char = b'\xef\xbf\xbd'.decode('utf-8') + if replacement_char in metadata: + tmpl = "{self.path} could not be properly decoded in UTF-8" + msg = tmpl.format(**locals()) + warnings.warn(msg) + + def get_metadata_lines(self, name): + return yield_lines(self.get_metadata(name)) + + +class PathMetadata(DefaultProvider): + """Metadata provider for egg directories + + Usage:: + + # Development eggs: + + egg_info = "/path/to/PackageName.egg-info" + base_dir = os.path.dirname(egg_info) + metadata = PathMetadata(base_dir, egg_info) + dist_name = os.path.splitext(os.path.basename(egg_info))[0] + dist = Distribution(basedir, project_name=dist_name, metadata=metadata) + + # Unpacked egg directories: + + egg_path = "/path/to/PackageName-ver-pyver-etc.egg" + metadata = PathMetadata(egg_path, os.path.join(egg_path,'EGG-INFO')) + dist = Distribution.from_filename(egg_path, metadata=metadata) + """ + + def __init__(self, path, egg_info): + self.module_path = path + self.egg_info = egg_info + + +class EggMetadata(ZipProvider): + """Metadata provider for .egg files""" + + def __init__(self, importer): + """Create a metadata provider from a zipimporter""" + + self.zip_pre = importer.archive + os.sep + self.loader = importer + if importer.prefix: + self.module_path = os.path.join(importer.archive, importer.prefix) + else: + self.module_path = importer.archive + self._setup_prefix() + + +_declare_state('dict', _distribution_finders={}) + + +def register_finder(importer_type, distribution_finder): + """Register `distribution_finder` to find distributions in sys.path items + + `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item + handler), and `distribution_finder` is a callable that, passed a path + item and the importer instance, yields ``Distribution`` instances found on + that path item. See ``pkg_resources.find_on_path`` for an example.""" + _distribution_finders[importer_type] = distribution_finder + + +def find_distributions(path_item, only=False): + """Yield distributions accessible via `path_item`""" + importer = get_importer(path_item) + finder = _find_adapter(_distribution_finders, importer) + return finder(importer, path_item, only) + + +def find_eggs_in_zip(importer, path_item, only=False): + """ + Find eggs in zip files; possibly multiple nested eggs. + """ + if importer.archive.endswith('.whl'): + # wheels are not supported with this finder + # they don't have PKG-INFO metadata, and won't ever contain eggs + return + metadata = EggMetadata(importer) + if metadata.has_metadata('PKG-INFO'): + yield Distribution.from_filename(path_item, metadata=metadata) + if only: + # don't yield nested distros + return + for subitem in metadata.resource_listdir(''): + if _is_egg_path(subitem): + subpath = os.path.join(path_item, subitem) + dists = find_eggs_in_zip(zipimport.zipimporter(subpath), subpath) + for dist in dists: + yield dist + elif subitem.lower().endswith('.dist-info'): + subpath = os.path.join(path_item, subitem) + submeta = EggMetadata(zipimport.zipimporter(subpath)) + submeta.egg_info = subpath + yield Distribution.from_location(path_item, subitem, submeta) + + +register_finder(zipimport.zipimporter, find_eggs_in_zip) + + +def find_nothing(importer, path_item, only=False): + return () + + +register_finder(object, find_nothing) + + +def _by_version_descending(names): + """ + Given a list of filenames, return them in descending order + by version number. + + >>> names = 'bar', 'foo', 'Python-2.7.10.egg', 'Python-2.7.2.egg' + >>> _by_version_descending(names) + ['Python-2.7.10.egg', 'Python-2.7.2.egg', 'foo', 'bar'] + >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.egg' + >>> _by_version_descending(names) + ['Setuptools-1.2.3.egg', 'Setuptools-1.2.3b1.egg'] + >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.post1.egg' + >>> _by_version_descending(names) + ['Setuptools-1.2.3.post1.egg', 'Setuptools-1.2.3b1.egg'] + """ + def _by_version(name): + """ + Parse each component of the filename + """ + name, ext = os.path.splitext(name) + parts = itertools.chain(name.split('-'), [ext]) + return [packaging.version.parse(part) for part in parts] + + return sorted(names, key=_by_version, reverse=True) + + +def find_on_path(importer, path_item, only=False): + """Yield distributions accessible on a sys.path directory""" + path_item = _normalize_cached(path_item) + + if _is_unpacked_egg(path_item): + yield Distribution.from_filename( + path_item, metadata=PathMetadata( + path_item, os.path.join(path_item, 'EGG-INFO') + ) + ) + return + + entries = safe_listdir(path_item) + + # for performance, before sorting by version, + # screen entries for only those that will yield + # distributions + filtered = ( + entry + for entry in entries + if dist_factory(path_item, entry, only) + ) + + # scan for .egg and .egg-info in directory + path_item_entries = _by_version_descending(filtered) + for entry in path_item_entries: + fullpath = os.path.join(path_item, entry) + factory = dist_factory(path_item, entry, only) + for dist in factory(fullpath): + yield dist + + +def dist_factory(path_item, entry, only): + """ + Return a dist_factory for a path_item and entry + """ + lower = entry.lower() + is_meta = any(map(lower.endswith, ('.egg-info', '.dist-info'))) + return ( + distributions_from_metadata + if is_meta else + find_distributions + if not only and _is_egg_path(entry) else + resolve_egg_link + if not only and lower.endswith('.egg-link') else + NoDists() + ) + + +class NoDists: + """ + >>> bool(NoDists()) + False + + >>> list(NoDists()('anything')) + [] + """ + def __bool__(self): + return False + if six.PY2: + __nonzero__ = __bool__ + + def __call__(self, fullpath): + return iter(()) + + +def safe_listdir(path): + """ + Attempt to list contents of path, but suppress some exceptions. + """ + try: + return os.listdir(path) + except (PermissionError, NotADirectoryError): + pass + except OSError as e: + # Ignore the directory if does not exist, not a directory or + # permission denied + ignorable = ( + e.errno in (errno.ENOTDIR, errno.EACCES, errno.ENOENT) + # Python 2 on Windows needs to be handled this way :( + or getattr(e, "winerror", None) == 267 + ) + if not ignorable: + raise + return () + + +def distributions_from_metadata(path): + root = os.path.dirname(path) + if os.path.isdir(path): + if len(os.listdir(path)) == 0: + # empty metadata dir; skip + return + metadata = PathMetadata(root, path) + else: + metadata = FileMetadata(path) + entry = os.path.basename(path) + yield Distribution.from_location( + root, entry, metadata, precedence=DEVELOP_DIST, + ) + + +def non_empty_lines(path): + """ + Yield non-empty lines from file at path + """ + with open(path) as f: + for line in f: + line = line.strip() + if line: + yield line + + +def resolve_egg_link(path): + """ + Given a path to an .egg-link, resolve distributions + present in the referenced path. + """ + referenced_paths = non_empty_lines(path) + resolved_paths = ( + os.path.join(os.path.dirname(path), ref) + for ref in referenced_paths + ) + dist_groups = map(find_distributions, resolved_paths) + return next(dist_groups, ()) + + +register_finder(pkgutil.ImpImporter, find_on_path) + +if hasattr(importlib_machinery, 'FileFinder'): + register_finder(importlib_machinery.FileFinder, find_on_path) + +_declare_state('dict', _namespace_handlers={}) +_declare_state('dict', _namespace_packages={}) + + +def register_namespace_handler(importer_type, namespace_handler): + """Register `namespace_handler` to declare namespace packages + + `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item + handler), and `namespace_handler` is a callable like this:: + + def namespace_handler(importer, path_entry, moduleName, module): + # return a path_entry to use for child packages + + Namespace handlers are only called if the importer object has already + agreed that it can handle the relevant path item, and they should only + return a subpath if the module __path__ does not already contain an + equivalent subpath. For an example namespace handler, see + ``pkg_resources.file_ns_handler``. + """ + _namespace_handlers[importer_type] = namespace_handler + + +def _handle_ns(packageName, path_item): + """Ensure that named package includes a subpath of path_item (if needed)""" + + importer = get_importer(path_item) + if importer is None: + return None + + # capture warnings due to #1111 + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + loader = importer.find_module(packageName) + + if loader is None: + return None + module = sys.modules.get(packageName) + if module is None: + module = sys.modules[packageName] = types.ModuleType(packageName) + module.__path__ = [] + _set_parent_ns(packageName) + elif not hasattr(module, '__path__'): + raise TypeError("Not a package:", packageName) + handler = _find_adapter(_namespace_handlers, importer) + subpath = handler(importer, path_item, packageName, module) + if subpath is not None: + path = module.__path__ + path.append(subpath) + loader.load_module(packageName) + _rebuild_mod_path(path, packageName, module) + return subpath + + +def _rebuild_mod_path(orig_path, package_name, module): + """ + Rebuild module.__path__ ensuring that all entries are ordered + corresponding to their sys.path order + """ + sys_path = [_normalize_cached(p) for p in sys.path] + + def safe_sys_path_index(entry): + """ + Workaround for #520 and #513. + """ + try: + return sys_path.index(entry) + except ValueError: + return float('inf') + + def position_in_sys_path(path): + """ + Return the ordinal of the path based on its position in sys.path + """ + path_parts = path.split(os.sep) + module_parts = package_name.count('.') + 1 + parts = path_parts[:-module_parts] + return safe_sys_path_index(_normalize_cached(os.sep.join(parts))) + + new_path = sorted(orig_path, key=position_in_sys_path) + new_path = [_normalize_cached(p) for p in new_path] + + if isinstance(module.__path__, list): + module.__path__[:] = new_path + else: + module.__path__ = new_path + + +def declare_namespace(packageName): + """Declare that package 'packageName' is a namespace package""" + + _imp.acquire_lock() + try: + if packageName in _namespace_packages: + return + + path = sys.path + parent, _, _ = packageName.rpartition('.') + + if parent: + declare_namespace(parent) + if parent not in _namespace_packages: + __import__(parent) + try: + path = sys.modules[parent].__path__ + except AttributeError: + raise TypeError("Not a package:", parent) + + # Track what packages are namespaces, so when new path items are added, + # they can be updated + _namespace_packages.setdefault(parent or None, []).append(packageName) + _namespace_packages.setdefault(packageName, []) + + for path_item in path: + # Ensure all the parent's path items are reflected in the child, + # if they apply + _handle_ns(packageName, path_item) + + finally: + _imp.release_lock() + + +def fixup_namespace_packages(path_item, parent=None): + """Ensure that previously-declared namespace packages include path_item""" + _imp.acquire_lock() + try: + for package in _namespace_packages.get(parent, ()): + subpath = _handle_ns(package, path_item) + if subpath: + fixup_namespace_packages(subpath, package) + finally: + _imp.release_lock() + + +def file_ns_handler(importer, path_item, packageName, module): + """Compute an ns-package subpath for a filesystem or zipfile importer""" + + subpath = os.path.join(path_item, packageName.split('.')[-1]) + normalized = _normalize_cached(subpath) + for item in module.__path__: + if _normalize_cached(item) == normalized: + break + else: + # Only return the path if it's not already there + return subpath + + +register_namespace_handler(pkgutil.ImpImporter, file_ns_handler) +register_namespace_handler(zipimport.zipimporter, file_ns_handler) + +if hasattr(importlib_machinery, 'FileFinder'): + register_namespace_handler(importlib_machinery.FileFinder, file_ns_handler) + + +def null_ns_handler(importer, path_item, packageName, module): + return None + + +register_namespace_handler(object, null_ns_handler) + + +def normalize_path(filename): + """Normalize a file/dir name for comparison purposes""" + return os.path.normcase(os.path.realpath(os.path.normpath(_cygwin_patch(filename)))) + + +def _cygwin_patch(filename): # pragma: nocover + """ + Contrary to POSIX 2008, on Cygwin, getcwd (3) contains + symlink components. Using + os.path.abspath() works around this limitation. A fix in os.getcwd() + would probably better, in Cygwin even more so, except + that this seems to be by design... + """ + return os.path.abspath(filename) if sys.platform == 'cygwin' else filename + + +def _normalize_cached(filename, _cache={}): + try: + return _cache[filename] + except KeyError: + _cache[filename] = result = normalize_path(filename) + return result + + +def _is_egg_path(path): + """ + Determine if given path appears to be an egg. + """ + return path.lower().endswith('.egg') + + +def _is_unpacked_egg(path): + """ + Determine if given path appears to be an unpacked egg. + """ + return ( + _is_egg_path(path) and + os.path.isfile(os.path.join(path, 'EGG-INFO', 'PKG-INFO')) + ) + + +def _set_parent_ns(packageName): + parts = packageName.split('.') + name = parts.pop() + if parts: + parent = '.'.join(parts) + setattr(sys.modules[parent], name, sys.modules[packageName]) + + +def yield_lines(strs): + """Yield non-empty/non-comment lines of a string or sequence""" + if isinstance(strs, six.string_types): + for s in strs.splitlines(): + s = s.strip() + # skip blank lines/comments + if s and not s.startswith('#'): + yield s + else: + for ss in strs: + for s in yield_lines(ss): + yield s + + +MODULE = re.compile(r"\w+(\.\w+)*$").match +EGG_NAME = re.compile( + r""" + (?P<name>[^-]+) ( + -(?P<ver>[^-]+) ( + -py(?P<pyver>[^-]+) ( + -(?P<plat>.+) + )? + )? + )? + """, + re.VERBOSE | re.IGNORECASE, +).match + + +class EntryPoint: + """Object representing an advertised importable object""" + + def __init__(self, name, module_name, attrs=(), extras=(), dist=None): + if not MODULE(module_name): + raise ValueError("Invalid module name", module_name) + self.name = name + self.module_name = module_name + self.attrs = tuple(attrs) + self.extras = tuple(extras) + self.dist = dist + + def __str__(self): + s = "%s = %s" % (self.name, self.module_name) + if self.attrs: + s += ':' + '.'.join(self.attrs) + if self.extras: + s += ' [%s]' % ','.join(self.extras) + return s + + def __repr__(self): + return "EntryPoint.parse(%r)" % str(self) + + def load(self, require=True, *args, **kwargs): + """ + Require packages for this EntryPoint, then resolve it. + """ + if not require or args or kwargs: + warnings.warn( + "Parameters to load are deprecated. Call .resolve and " + ".require separately.", + PkgResourcesDeprecationWarning, + stacklevel=2, + ) + if require: + self.require(*args, **kwargs) + return self.resolve() + + def resolve(self): + """ + Resolve the entry point from its module and attrs. + """ + module = __import__(self.module_name, fromlist=['__name__'], level=0) + try: + return functools.reduce(getattr, self.attrs, module) + except AttributeError as exc: + raise ImportError(str(exc)) + + def require(self, env=None, installer=None): + if self.extras and not self.dist: + raise UnknownExtra("Can't require() without a distribution", self) + + # Get the requirements for this entry point with all its extras and + # then resolve them. We have to pass `extras` along when resolving so + # that the working set knows what extras we want. Otherwise, for + # dist-info distributions, the working set will assume that the + # requirements for that extra are purely optional and skip over them. + reqs = self.dist.requires(self.extras) + items = working_set.resolve(reqs, env, installer, extras=self.extras) + list(map(working_set.add, items)) + + pattern = re.compile( + r'\s*' + r'(?P<name>.+?)\s*' + r'=\s*' + r'(?P<module>[\w.]+)\s*' + r'(:\s*(?P<attr>[\w.]+))?\s*' + r'(?P<extras>\[.*\])?\s*$' + ) + + @classmethod + def parse(cls, src, dist=None): + """Parse a single entry point from string `src` + + Entry point syntax follows the form:: + + name = some.module:some.attr [extra1, extra2] + + The entry name and module name are required, but the ``:attrs`` and + ``[extras]`` parts are optional + """ + m = cls.pattern.match(src) + if not m: + msg = "EntryPoint must be in 'name=module:attrs [extras]' format" + raise ValueError(msg, src) + res = m.groupdict() + extras = cls._parse_extras(res['extras']) + attrs = res['attr'].split('.') if res['attr'] else () + return cls(res['name'], res['module'], attrs, extras, dist) + + @classmethod + def _parse_extras(cls, extras_spec): + if not extras_spec: + return () + req = Requirement.parse('x' + extras_spec) + if req.specs: + raise ValueError() + return req.extras + + @classmethod + def parse_group(cls, group, lines, dist=None): + """Parse an entry point group""" + if not MODULE(group): + raise ValueError("Invalid group name", group) + this = {} + for line in yield_lines(lines): + ep = cls.parse(line, dist) + if ep.name in this: + raise ValueError("Duplicate entry point", group, ep.name) + this[ep.name] = ep + return this + + @classmethod + def parse_map(cls, data, dist=None): + """Parse a map of entry point groups""" + if isinstance(data, dict): + data = data.items() + else: + data = split_sections(data) + maps = {} + for group, lines in data: + if group is None: + if not lines: + continue + raise ValueError("Entry points must be listed in groups") + group = group.strip() + if group in maps: + raise ValueError("Duplicate group name", group) + maps[group] = cls.parse_group(group, lines, dist) + return maps + + +def _remove_md5_fragment(location): + if not location: + return '' + parsed = urllib.parse.urlparse(location) + if parsed[-1].startswith('md5='): + return urllib.parse.urlunparse(parsed[:-1] + ('',)) + return location + + +def _version_from_file(lines): + """ + Given an iterable of lines from a Metadata file, return + the value of the Version field, if present, or None otherwise. + """ + def is_version_line(line): + return line.lower().startswith('version:') + version_lines = filter(is_version_line, lines) + line = next(iter(version_lines), '') + _, _, value = line.partition(':') + return safe_version(value.strip()) or None + + +class Distribution: + """Wrap an actual or potential sys.path entry w/metadata""" + PKG_INFO = 'PKG-INFO' + + def __init__( + self, location=None, metadata=None, project_name=None, + version=None, py_version=PY_MAJOR, platform=None, + precedence=EGG_DIST): + self.project_name = safe_name(project_name or 'Unknown') + if version is not None: + self._version = safe_version(version) + self.py_version = py_version + self.platform = platform + self.location = location + self.precedence = precedence + self._provider = metadata or empty_provider + + @classmethod + def from_location(cls, location, basename, metadata=None, **kw): + project_name, version, py_version, platform = [None] * 4 + basename, ext = os.path.splitext(basename) + if ext.lower() in _distributionImpl: + cls = _distributionImpl[ext.lower()] + + match = EGG_NAME(basename) + if match: + project_name, version, py_version, platform = match.group( + 'name', 'ver', 'pyver', 'plat' + ) + return cls( + location, metadata, project_name=project_name, version=version, + py_version=py_version, platform=platform, **kw + )._reload_version() + + def _reload_version(self): + return self + + @property + def hashcmp(self): + return ( + self.parsed_version, + self.precedence, + self.key, + _remove_md5_fragment(self.location), + self.py_version or '', + self.platform or '', + ) + + def __hash__(self): + return hash(self.hashcmp) + + def __lt__(self, other): + return self.hashcmp < other.hashcmp + + def __le__(self, other): + return self.hashcmp <= other.hashcmp + + def __gt__(self, other): + return self.hashcmp > other.hashcmp + + def __ge__(self, other): + return self.hashcmp >= other.hashcmp + + def __eq__(self, other): + if not isinstance(other, self.__class__): + # It's not a Distribution, so they are not equal + return False + return self.hashcmp == other.hashcmp + + def __ne__(self, other): + return not self == other + + # These properties have to be lazy so that we don't have to load any + # metadata until/unless it's actually needed. (i.e., some distributions + # may not know their name or version without loading PKG-INFO) + + @property + def key(self): + try: + return self._key + except AttributeError: + self._key = key = self.project_name.lower() + return key + + @property + def parsed_version(self): + if not hasattr(self, "_parsed_version"): + self._parsed_version = parse_version(self.version) + + return self._parsed_version + + def _warn_legacy_version(self): + LV = packaging.version.LegacyVersion + is_legacy = isinstance(self._parsed_version, LV) + if not is_legacy: + return + + # While an empty version is technically a legacy version and + # is not a valid PEP 440 version, it's also unlikely to + # actually come from someone and instead it is more likely that + # it comes from setuptools attempting to parse a filename and + # including it in the list. So for that we'll gate this warning + # on if the version is anything at all or not. + if not self.version: + return + + tmpl = textwrap.dedent(""" + '{project_name} ({version})' is being parsed as a legacy, + non PEP 440, + version. You may find odd behavior and sort order. + In particular it will be sorted as less than 0.0. It + is recommended to migrate to PEP 440 compatible + versions. + """).strip().replace('\n', ' ') + + warnings.warn(tmpl.format(**vars(self)), PEP440Warning) + + @property + def version(self): + try: + return self._version + except AttributeError: + version = _version_from_file(self._get_metadata(self.PKG_INFO)) + if version is None: + tmpl = "Missing 'Version:' header and/or %s file" + raise ValueError(tmpl % self.PKG_INFO, self) + return version + + @property + def _dep_map(self): + """ + A map of extra to its list of (direct) requirements + for this distribution, including the null extra. + """ + try: + return self.__dep_map + except AttributeError: + self.__dep_map = self._filter_extras(self._build_dep_map()) + return self.__dep_map + + @staticmethod + def _filter_extras(dm): + """ + Given a mapping of extras to dependencies, strip off + environment markers and filter out any dependencies + not matching the markers. + """ + for extra in list(filter(None, dm)): + new_extra = extra + reqs = dm.pop(extra) + new_extra, _, marker = extra.partition(':') + fails_marker = marker and ( + invalid_marker(marker) + or not evaluate_marker(marker) + ) + if fails_marker: + reqs = [] + new_extra = safe_extra(new_extra) or None + + dm.setdefault(new_extra, []).extend(reqs) + return dm + + def _build_dep_map(self): + dm = {} + for name in 'requires.txt', 'depends.txt': + for extra, reqs in split_sections(self._get_metadata(name)): + dm.setdefault(extra, []).extend(parse_requirements(reqs)) + return dm + + def requires(self, extras=()): + """List of Requirements needed for this distro if `extras` are used""" + dm = self._dep_map + deps = [] + deps.extend(dm.get(None, ())) + for ext in extras: + try: + deps.extend(dm[safe_extra(ext)]) + except KeyError: + raise UnknownExtra( + "%s has no such extra feature %r" % (self, ext) + ) + return deps + + def _get_metadata(self, name): + if self.has_metadata(name): + for line in self.get_metadata_lines(name): + yield line + + def activate(self, path=None, replace=False): + """Ensure distribution is importable on `path` (default=sys.path)""" + if path is None: + path = sys.path + self.insert_on(path, replace=replace) + if path is sys.path: + fixup_namespace_packages(self.location) + for pkg in self._get_metadata('namespace_packages.txt'): + if pkg in sys.modules: + declare_namespace(pkg) + + def egg_name(self): + """Return what this distribution's standard .egg filename should be""" + filename = "%s-%s-py%s" % ( + to_filename(self.project_name), to_filename(self.version), + self.py_version or PY_MAJOR + ) + + if self.platform: + filename += '-' + self.platform + return filename + + def __repr__(self): + if self.location: + return "%s (%s)" % (self, self.location) + else: + return str(self) + + def __str__(self): + try: + version = getattr(self, 'version', None) + except ValueError: + version = None + version = version or "[unknown version]" + return "%s %s" % (self.project_name, version) + + def __getattr__(self, attr): + """Delegate all unrecognized public attributes to .metadata provider""" + if attr.startswith('_'): + raise AttributeError(attr) + return getattr(self._provider, attr) + + def __dir__(self): + return list( + set(super(Distribution, self).__dir__()) + | set( + attr for attr in self._provider.__dir__() + if not attr.startswith('_') + ) + ) + + if not hasattr(object, '__dir__'): + # python 2.7 not supported + del __dir__ + + @classmethod + def from_filename(cls, filename, metadata=None, **kw): + return cls.from_location( + _normalize_cached(filename), os.path.basename(filename), metadata, + **kw + ) + + def as_requirement(self): + """Return a ``Requirement`` that matches this distribution exactly""" + if isinstance(self.parsed_version, packaging.version.Version): + spec = "%s==%s" % (self.project_name, self.parsed_version) + else: + spec = "%s===%s" % (self.project_name, self.parsed_version) + + return Requirement.parse(spec) + + def load_entry_point(self, group, name): + """Return the `name` entry point of `group` or raise ImportError""" + ep = self.get_entry_info(group, name) + if ep is None: + raise ImportError("Entry point %r not found" % ((group, name),)) + return ep.load() + + def get_entry_map(self, group=None): + """Return the entry point map for `group`, or the full entry map""" + try: + ep_map = self._ep_map + except AttributeError: + ep_map = self._ep_map = EntryPoint.parse_map( + self._get_metadata('entry_points.txt'), self + ) + if group is not None: + return ep_map.get(group, {}) + return ep_map + + def get_entry_info(self, group, name): + """Return the EntryPoint object for `group`+`name`, or ``None``""" + return self.get_entry_map(group).get(name) + + def insert_on(self, path, loc=None, replace=False): + """Ensure self.location is on path + + If replace=False (default): + - If location is already in path anywhere, do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent. + - Else: add to the end of path. + If replace=True: + - If location is already on path anywhere (not eggs) + or higher priority than its parent (eggs) + do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent, + removing any lower-priority entries. + - Else: add it to the front of path. + """ + + loc = loc or self.location + if not loc: + return + + nloc = _normalize_cached(loc) + bdir = os.path.dirname(nloc) + npath = [(p and _normalize_cached(p) or p) for p in path] + + for p, item in enumerate(npath): + if item == nloc: + if replace: + break + else: + # don't modify path (even removing duplicates) if + # found and not replace + return + elif item == bdir and self.precedence == EGG_DIST: + # if it's an .egg, give it precedence over its directory + # UNLESS it's already been added to sys.path and replace=False + if (not replace) and nloc in npath[p:]: + return + if path is sys.path: + self.check_version_conflict() + path.insert(p, loc) + npath.insert(p, nloc) + break + else: + if path is sys.path: + self.check_version_conflict() + if replace: + path.insert(0, loc) + else: + path.append(loc) + return + + # p is the spot where we found or inserted loc; now remove duplicates + while True: + try: + np = npath.index(nloc, p + 1) + except ValueError: + break + else: + del npath[np], path[np] + # ha! + p = np + + return + + def check_version_conflict(self): + if self.key == 'setuptools': + # ignore the inevitable setuptools self-conflicts :( + return + + nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt')) + loc = normalize_path(self.location) + for modname in self._get_metadata('top_level.txt'): + if (modname not in sys.modules or modname in nsp + or modname in _namespace_packages): + continue + if modname in ('pkg_resources', 'setuptools', 'site'): + continue + fn = getattr(sys.modules[modname], '__file__', None) + if fn and (normalize_path(fn).startswith(loc) or + fn.startswith(self.location)): + continue + issue_warning( + "Module %s was already imported from %s, but %s is being added" + " to sys.path" % (modname, fn, self.location), + ) + + def has_version(self): + try: + self.version + except ValueError: + issue_warning("Unbuilt egg for " + repr(self)) + return False + return True + + def clone(self, **kw): + """Copy this distribution, substituting in any changed keyword args""" + names = 'project_name version py_version platform location precedence' + for attr in names.split(): + kw.setdefault(attr, getattr(self, attr, None)) + kw.setdefault('metadata', self._provider) + return self.__class__(**kw) + + @property + def extras(self): + return [dep for dep in self._dep_map if dep] + + +class EggInfoDistribution(Distribution): + def _reload_version(self): + """ + Packages installed by distutils (e.g. numpy or scipy), + which uses an old safe_version, and so + their version numbers can get mangled when + converted to filenames (e.g., 1.11.0.dev0+2329eae to + 1.11.0.dev0_2329eae). These distributions will not be + parsed properly + downstream by Distribution and safe_version, so + take an extra step and try to get the version number from + the metadata file itself instead of the filename. + """ + md_version = _version_from_file(self._get_metadata(self.PKG_INFO)) + if md_version: + self._version = md_version + return self + + +class DistInfoDistribution(Distribution): + """ + Wrap an actual or potential sys.path entry + w/metadata, .dist-info style. + """ + PKG_INFO = 'METADATA' + EQEQ = re.compile(r"([\(,])\s*(\d.*?)\s*([,\)])") + + @property + def _parsed_pkg_info(self): + """Parse and cache metadata""" + try: + return self._pkg_info + except AttributeError: + metadata = self.get_metadata(self.PKG_INFO) + self._pkg_info = email.parser.Parser().parsestr(metadata) + return self._pkg_info + + @property + def _dep_map(self): + try: + return self.__dep_map + except AttributeError: + self.__dep_map = self._compute_dependencies() + return self.__dep_map + + def _compute_dependencies(self): + """Recompute this distribution's dependencies.""" + dm = self.__dep_map = {None: []} + + reqs = [] + # Including any condition expressions + for req in self._parsed_pkg_info.get_all('Requires-Dist') or []: + reqs.extend(parse_requirements(req)) + + def reqs_for_extra(extra): + for req in reqs: + if not req.marker or req.marker.evaluate({'extra': extra}): + yield req + + common = frozenset(reqs_for_extra(None)) + dm[None].extend(common) + + for extra in self._parsed_pkg_info.get_all('Provides-Extra') or []: + s_extra = safe_extra(extra.strip()) + dm[s_extra] = list(frozenset(reqs_for_extra(extra)) - common) + + return dm + + +_distributionImpl = { + '.egg': Distribution, + '.egg-info': EggInfoDistribution, + '.dist-info': DistInfoDistribution, +} + + +def issue_warning(*args, **kw): + level = 1 + g = globals() + try: + # find the first stack frame that is *not* code in + # the pkg_resources module, to use for the warning + while sys._getframe(level).f_globals is g: + level += 1 + except ValueError: + pass + warnings.warn(stacklevel=level + 1, *args, **kw) + + +class RequirementParseError(ValueError): + def __str__(self): + return ' '.join(self.args) + + +def parse_requirements(strs): + """Yield ``Requirement`` objects for each specification in `strs` + + `strs` must be a string, or a (possibly-nested) iterable thereof. + """ + # create a steppable iterator, so we can handle \-continuations + lines = iter(yield_lines(strs)) + + for line in lines: + # Drop comments -- a hash without a space may be in a URL. + if ' #' in line: + line = line[:line.find(' #')] + # If there is a line continuation, drop it, and append the next line. + if line.endswith('\\'): + line = line[:-2].strip() + try: + line += next(lines) + except StopIteration: + return + yield Requirement(line) + + +class Requirement(packaging.requirements.Requirement): + def __init__(self, requirement_string): + """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!""" + try: + super(Requirement, self).__init__(requirement_string) + except packaging.requirements.InvalidRequirement as e: + raise RequirementParseError(str(e)) + self.unsafe_name = self.name + project_name = safe_name(self.name) + self.project_name, self.key = project_name, project_name.lower() + self.specs = [ + (spec.operator, spec.version) for spec in self.specifier] + self.extras = tuple(map(safe_extra, self.extras)) + self.hashCmp = ( + self.key, + self.specifier, + frozenset(self.extras), + str(self.marker) if self.marker else None, + ) + self.__hash = hash(self.hashCmp) + + def __eq__(self, other): + return ( + isinstance(other, Requirement) and + self.hashCmp == other.hashCmp + ) + + def __ne__(self, other): + return not self == other + + def __contains__(self, item): + if isinstance(item, Distribution): + if item.key != self.key: + return False + + item = item.version + + # Allow prereleases always in order to match the previous behavior of + # this method. In the future this should be smarter and follow PEP 440 + # more accurately. + return self.specifier.contains(item, prereleases=True) + + def __hash__(self): + return self.__hash + + def __repr__(self): + return "Requirement.parse(%r)" % str(self) + + @staticmethod + def parse(s): + req, = parse_requirements(s) + return req + + +def _always_object(classes): + """ + Ensure object appears in the mro even + for old-style classes. + """ + if object not in classes: + return classes + (object,) + return classes + + +def _find_adapter(registry, ob): + """Return an adapter factory for `ob` from `registry`""" + types = _always_object(inspect.getmro(getattr(ob, '__class__', type(ob)))) + for t in types: + if t in registry: + return registry[t] + + +def ensure_directory(path): + """Ensure that the parent directory of `path` exists""" + dirname = os.path.dirname(path) + py31compat.makedirs(dirname, exist_ok=True) + + +def _bypass_ensure_directory(path): + """Sandbox-bypassing version of ensure_directory()""" + if not WRITE_SUPPORT: + raise IOError('"os.mkdir" not supported on this platform.') + dirname, filename = split(path) + if dirname and filename and not isdir(dirname): + _bypass_ensure_directory(dirname) + try: + mkdir(dirname, 0o755) + except FileExistsError: + pass + + +def split_sections(s): + """Split a string or iterable thereof into (section, content) pairs + + Each ``section`` is a stripped version of the section header ("[section]") + and each ``content`` is a list of stripped lines excluding blank lines and + comment-only lines. If there are any such lines before the first section + header, they're returned in a first ``section`` of ``None``. + """ + section = None + content = [] + for line in yield_lines(s): + if line.startswith("["): + if line.endswith("]"): + if section or content: + yield section, content + section = line[1:-1].strip() + content = [] + else: + raise ValueError("Invalid section heading", line) + else: + content.append(line) + + # wrap up last segment + yield section, content + + +def _mkstemp(*args, **kw): + old_open = os.open + try: + # temporarily bypass sandboxing + os.open = os_open + return tempfile.mkstemp(*args, **kw) + finally: + # and then put it back + os.open = old_open + + +# Silence the PEP440Warning by default, so that end users don't get hit by it +# randomly just because they use pkg_resources. We want to append the rule +# because we want earlier uses of filterwarnings to take precedence over this +# one. +warnings.filterwarnings("ignore", category=PEP440Warning, append=True) + + +# from jaraco.functools 1.3 +def _call_aside(f, *args, **kwargs): + f(*args, **kwargs) + return f + + +@_call_aside +def _initialize(g=globals()): + "Set up global resource manager (deliberately not state-saved)" + manager = ResourceManager() + g['_manager'] = manager + g.update( + (name, getattr(manager, name)) + for name in dir(manager) + if not name.startswith('_') + ) + + +@_call_aside +def _initialize_master_working_set(): + """ + Prepare the master working set and make the ``require()`` + API available. + + This function has explicit effects on the global state + of pkg_resources. It is intended to be invoked once at + the initialization of this module. + + Invocation by other packages is unsupported and done + at their own risk. + """ + working_set = WorkingSet._build_master() + _declare_state('object', working_set=working_set) + + require = working_set.require + iter_entry_points = working_set.iter_entry_points + add_activation_listener = working_set.subscribe + run_script = working_set.run_script + # backward compatibility + run_main = run_script + # Activate all distributions already on sys.path with replace=False and + # ensure that all distributions added to the working set in the future + # (e.g. by calling ``require()``) will get activated as well, + # with higher priority (replace=True). + tuple( + dist.activate(replace=False) + for dist in working_set + ) + add_activation_listener( + lambda dist: dist.activate(replace=True), + existing=False, + ) + working_set.entries = [] + # match order + list(map(working_set.add_entry, sys.path)) + globals().update(locals()) + +class PkgResourcesDeprecationWarning(Warning): + """ + Base class for warning about deprecations in ``pkg_resources`` + + This class is not derived from ``DeprecationWarning``, and as such is + visible by default. + """ diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/__init__.py b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/appdirs.py b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/appdirs.py new file mode 100644 index 0000000..ae67001 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/appdirs.py @@ -0,0 +1,608 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2010 ActiveState Software Inc. +# Copyright (c) 2013 Eddy Petrișor + +"""Utilities for determining application-specific dirs. + +See <http://github.com/ActiveState/appdirs> for details and usage. +""" +# Dev Notes: +# - MSDN on where to store app data files: +# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 +# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html +# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + +__version_info__ = (1, 4, 3) +__version__ = '.'.join(map(str, __version_info__)) + + +import sys +import os + +PY3 = sys.version_info[0] == 3 + +if PY3: + unicode = str + +if sys.platform.startswith('java'): + import platform + os_name = platform.java_ver()[3][0] + if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc. + system = 'win32' + elif os_name.startswith('Mac'): # "Mac OS X", etc. + system = 'darwin' + else: # "Linux", "SunOS", "FreeBSD", etc. + # Setting this to "linux2" is not ideal, but only Windows or Mac + # are actually checked for and the rest of the module expects + # *sys.platform* style strings. + system = 'linux2' +else: + system = sys.platform + + + +def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> + for a discussion of issues. + + Typical user data directories are: + Mac OS X: ~/Library/Application Support/<AppName> + Unix: ~/.local/share/<AppName> # or in $XDG_DATA_HOME, if defined + Win XP (not roaming): C:\Documents and Settings\<username>\Application Data\<AppAuthor>\<AppName> + Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName> + Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName> + Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName> + + For Unix, we follow the XDG spec and support $XDG_DATA_HOME. + That means, by default "~/.local/share/<AppName>". + """ + if system == "win32": + if appauthor is None: + appauthor = appname + const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" + path = os.path.normpath(_get_win_folder(const)) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('~/Library/Application Support/') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): + r"""Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of data dirs should be + returned. By default, the first item from XDG_DATA_DIRS is + returned, or '/usr/local/share/<AppName>', + if XDG_DATA_DIRS is not set + + Typical site data directories are: + Mac OS X: /Library/Application Support/<AppName> + Unix: /usr/local/share/<AppName> or /usr/share/<AppName> + Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName> + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7. + + For Unix, this is using the $XDG_DATA_DIRS[0] default. + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('/Library/Application Support') + if appname: + path = os.path.join(path, appname) + else: + # XDG default for $XDG_DATA_DIRS + # only first, if multipath is False + path = os.getenv('XDG_DATA_DIRS', + os.pathsep.join(['/usr/local/share', '/usr/share'])) + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + if appname and version: + path = os.path.join(path, version) + return path + + +def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific config dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> + for a discussion of issues. + + Typical user config directories are: + Mac OS X: same as user_data_dir + Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. + That means, by default "~/.config/<AppName>". + """ + if system in ["win32", "darwin"]: + path = user_data_dir(appname, appauthor, None, roaming) + else: + path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_config_dir(appname=None, appauthor=None, version=None, multipath=False): + r"""Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of config dirs should be + returned. By default, the first item from XDG_CONFIG_DIRS is + returned, or '/etc/xdg/<AppName>', if XDG_CONFIG_DIRS is not set + + Typical site config directories are: + Mac OS X: same as site_data_dir + Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in + $XDG_CONFIG_DIRS + Win *: same as site_data_dir + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + + For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system in ["win32", "darwin"]: + path = site_data_dir(appname, appauthor) + if appname and version: + path = os.path.join(path, version) + else: + # XDG default for $XDG_CONFIG_DIRS + # only first, if multipath is False + path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + +def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific cache dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Cache" to the base app data dir for Windows. See + discussion below. + + Typical user cache directories are: + Mac OS X: ~/Library/Caches/<AppName> + Unix: ~/.cache/<AppName> (XDG default) + Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache + Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache + + On Windows the only suggestion in the MSDN docs is that local settings go in + the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming + app data dir (the default returned by `user_data_dir` above). Apps typically + put cache data somewhere *under* the given dir here. Some examples: + ...\Mozilla\Firefox\Profiles\<ProfileName>\Cache + ...\Acme\SuperApp\Cache\1.0 + OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. + This can be disabled with the `opinion=False` option. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + if opinion: + path = os.path.join(path, "Cache") + elif system == 'darwin': + path = os.path.expanduser('~/Library/Caches') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def user_state_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific state dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> + for a discussion of issues. + + Typical user state directories are: + Mac OS X: same as user_data_dir + Unix: ~/.local/state/<AppName> # or in $XDG_STATE_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow this Debian proposal <https://wiki.debian.org/XDGBaseDirectorySpecification#state> + to extend the XDG spec and support $XDG_STATE_HOME. + + That means, by default "~/.local/state/<AppName>". + """ + if system in ["win32", "darwin"]: + path = user_data_dir(appname, appauthor, None, roaming) + else: + path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific log dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Logs" to the base app data dir for Windows, and "log" to the + base cache dir for Unix. See discussion below. + + Typical user log directories are: + Mac OS X: ~/Library/Logs/<AppName> + Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined + Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs + Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs + + On Windows the only suggestion in the MSDN docs is that local settings + go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in + examples of what some windows apps use for a logs dir.) + + OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` + value for Windows and appends "log" to the user cache dir for Unix. + This can be disabled with the `opinion=False` option. + """ + if system == "darwin": + path = os.path.join( + os.path.expanduser('~/Library/Logs'), + appname) + elif system == "win32": + path = user_data_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "Logs") + else: + path = user_cache_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "log") + if appname and version: + path = os.path.join(path, version) + return path + + +class AppDirs(object): + """Convenience wrapper for getting application dirs.""" + def __init__(self, appname=None, appauthor=None, version=None, + roaming=False, multipath=False): + self.appname = appname + self.appauthor = appauthor + self.version = version + self.roaming = roaming + self.multipath = multipath + + @property + def user_data_dir(self): + return user_data_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_data_dir(self): + return site_data_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_config_dir(self): + return user_config_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_config_dir(self): + return site_config_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_cache_dir(self): + return user_cache_dir(self.appname, self.appauthor, + version=self.version) + + @property + def user_state_dir(self): + return user_state_dir(self.appname, self.appauthor, + version=self.version) + + @property + def user_log_dir(self): + return user_log_dir(self.appname, self.appauthor, + version=self.version) + + +#---- internal support stuff + +def _get_win_folder_from_registry(csidl_name): + """This is a fallback technique at best. I'm not sure if using the + registry for this guarantees us the correct answer for all CSIDL_* + names. + """ + if PY3: + import winreg as _winreg + else: + import _winreg + + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + }[csidl_name] + + key = _winreg.OpenKey( + _winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" + ) + dir, type = _winreg.QueryValueEx(key, shell_folder_name) + return dir + + +def _get_win_folder_with_pywin32(csidl_name): + from win32com.shell import shellcon, shell + dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) + # Try to make this a unicode path because SHGetFolderPath does + # not return unicode strings when there is unicode data in the + # path. + try: + dir = unicode(dir) + + # Downgrade to short path name if have highbit chars. See + # <http://bugs.activestate.com/show_bug.cgi?id=85099>. + has_high_char = False + for c in dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + try: + import win32api + dir = win32api.GetShortPathName(dir) + except ImportError: + pass + except UnicodeError: + pass + return dir + + +def _get_win_folder_with_ctypes(csidl_name): + import ctypes + + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + }[csidl_name] + + buf = ctypes.create_unicode_buffer(1024) + ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if have highbit chars. See + # <http://bugs.activestate.com/show_bug.cgi?id=85099>. + has_high_char = False + for c in buf: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf2 = ctypes.create_unicode_buffer(1024) + if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + return buf.value + +def _get_win_folder_with_jna(csidl_name): + import array + from com.sun import jna + from com.sun.jna.platform import win32 + + buf_size = win32.WinDef.MAX_PATH * 2 + buf = array.zeros('c', buf_size) + shell = win32.Shell32.INSTANCE + shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf) + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + # Downgrade to short path name if have highbit chars. See + # <http://bugs.activestate.com/show_bug.cgi?id=85099>. + has_high_char = False + for c in dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf = array.zeros('c', buf_size) + kernel = win32.Kernel32.INSTANCE + if kernel.GetShortPathName(dir, buf, buf_size): + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + return dir + +if system == "win32": + try: + import win32com.shell + _get_win_folder = _get_win_folder_with_pywin32 + except ImportError: + try: + from ctypes import windll + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + try: + import com.sun.jna + _get_win_folder = _get_win_folder_with_jna + except ImportError: + _get_win_folder = _get_win_folder_from_registry + + +#---- self test code + +if __name__ == "__main__": + appname = "MyApp" + appauthor = "MyCompany" + + props = ("user_data_dir", + "user_config_dir", + "user_cache_dir", + "user_state_dir", + "user_log_dir", + "site_data_dir", + "site_config_dir") + + print("-- app dirs %s --" % __version__) + + print("-- app dirs (with optional 'version')") + dirs = AppDirs(appname, appauthor, version="1.0") + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'version')") + dirs = AppDirs(appname, appauthor) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'appauthor')") + dirs = AppDirs(appname) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (with disabled 'appauthor')") + dirs = AppDirs(appname, appauthor=False) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/__about__.py b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/__about__.py new file mode 100644 index 0000000..95d330e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/__about__.py @@ -0,0 +1,21 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +__all__ = [ + "__title__", "__summary__", "__uri__", "__version__", "__author__", + "__email__", "__license__", "__copyright__", +] + +__title__ = "packaging" +__summary__ = "Core utilities for Python packages" +__uri__ = "https://github.com/pypa/packaging" + +__version__ = "16.8" + +__author__ = "Donald Stufft and individual contributors" +__email__ = "donald@stufft.io" + +__license__ = "BSD or Apache License, Version 2.0" +__copyright__ = "Copyright 2014-2016 %s" % __author__ diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/__init__.py b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/__init__.py new file mode 100644 index 0000000..5ee6220 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/__init__.py @@ -0,0 +1,14 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +from .__about__ import ( + __author__, __copyright__, __email__, __license__, __summary__, __title__, + __uri__, __version__ +) + +__all__ = [ + "__title__", "__summary__", "__uri__", "__version__", "__author__", + "__email__", "__license__", "__copyright__", +] diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/_compat.py b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/_compat.py new file mode 100644 index 0000000..210bb80 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/_compat.py @@ -0,0 +1,30 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import sys + + +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 + +# flake8: noqa + +if PY3: + string_types = str, +else: + string_types = basestring, + + +def with_metaclass(meta, *bases): + """ + Create a base class with a metaclass. + """ + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/_structures.py b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/_structures.py new file mode 100644 index 0000000..ccc2786 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/_structures.py @@ -0,0 +1,68 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + + +class Infinity(object): + + def __repr__(self): + return "Infinity" + + def __hash__(self): + return hash(repr(self)) + + def __lt__(self, other): + return False + + def __le__(self, other): + return False + + def __eq__(self, other): + return isinstance(other, self.__class__) + + def __ne__(self, other): + return not isinstance(other, self.__class__) + + def __gt__(self, other): + return True + + def __ge__(self, other): + return True + + def __neg__(self): + return NegativeInfinity + +Infinity = Infinity() + + +class NegativeInfinity(object): + + def __repr__(self): + return "-Infinity" + + def __hash__(self): + return hash(repr(self)) + + def __lt__(self, other): + return True + + def __le__(self, other): + return True + + def __eq__(self, other): + return isinstance(other, self.__class__) + + def __ne__(self, other): + return not isinstance(other, self.__class__) + + def __gt__(self, other): + return False + + def __ge__(self, other): + return False + + def __neg__(self): + return Infinity + +NegativeInfinity = NegativeInfinity() diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/markers.py b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/markers.py new file mode 100644 index 0000000..892e578 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/markers.py @@ -0,0 +1,301 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import operator +import os +import platform +import sys + +from pkg_resources.extern.pyparsing import ParseException, ParseResults, stringStart, stringEnd +from pkg_resources.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedString +from pkg_resources.extern.pyparsing import Literal as L # noqa + +from ._compat import string_types +from .specifiers import Specifier, InvalidSpecifier + + +__all__ = [ + "InvalidMarker", "UndefinedComparison", "UndefinedEnvironmentName", + "Marker", "default_environment", +] + + +class InvalidMarker(ValueError): + """ + An invalid marker was found, users should refer to PEP 508. + """ + + +class UndefinedComparison(ValueError): + """ + An invalid operation was attempted on a value that doesn't support it. + """ + + +class UndefinedEnvironmentName(ValueError): + """ + A name was attempted to be used that does not exist inside of the + environment. + """ + + +class Node(object): + + def __init__(self, value): + self.value = value + + def __str__(self): + return str(self.value) + + def __repr__(self): + return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) + + def serialize(self): + raise NotImplementedError + + +class Variable(Node): + + def serialize(self): + return str(self) + + +class Value(Node): + + def serialize(self): + return '"{0}"'.format(self) + + +class Op(Node): + + def serialize(self): + return str(self) + + +VARIABLE = ( + L("implementation_version") | + L("platform_python_implementation") | + L("implementation_name") | + L("python_full_version") | + L("platform_release") | + L("platform_version") | + L("platform_machine") | + L("platform_system") | + L("python_version") | + L("sys_platform") | + L("os_name") | + L("os.name") | # PEP-345 + L("sys.platform") | # PEP-345 + L("platform.version") | # PEP-345 + L("platform.machine") | # PEP-345 + L("platform.python_implementation") | # PEP-345 + L("python_implementation") | # undocumented setuptools legacy + L("extra") +) +ALIASES = { + 'os.name': 'os_name', + 'sys.platform': 'sys_platform', + 'platform.version': 'platform_version', + 'platform.machine': 'platform_machine', + 'platform.python_implementation': 'platform_python_implementation', + 'python_implementation': 'platform_python_implementation' +} +VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) + +VERSION_CMP = ( + L("===") | + L("==") | + L(">=") | + L("<=") | + L("!=") | + L("~=") | + L(">") | + L("<") +) + +MARKER_OP = VERSION_CMP | L("not in") | L("in") +MARKER_OP.setParseAction(lambda s, l, t: Op(t[0])) + +MARKER_VALUE = QuotedString("'") | QuotedString('"') +MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0])) + +BOOLOP = L("and") | L("or") + +MARKER_VAR = VARIABLE | MARKER_VALUE + +MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR) +MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0])) + +LPAREN = L("(").suppress() +RPAREN = L(")").suppress() + +MARKER_EXPR = Forward() +MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN) +MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) + +MARKER = stringStart + MARKER_EXPR + stringEnd + + +def _coerce_parse_result(results): + if isinstance(results, ParseResults): + return [_coerce_parse_result(i) for i in results] + else: + return results + + +def _format_marker(marker, first=True): + assert isinstance(marker, (list, tuple, string_types)) + + # Sometimes we have a structure like [[...]] which is a single item list + # where the single item is itself it's own list. In that case we want skip + # the rest of this function so that we don't get extraneous () on the + # outside. + if (isinstance(marker, list) and len(marker) == 1 and + isinstance(marker[0], (list, tuple))): + return _format_marker(marker[0]) + + if isinstance(marker, list): + inner = (_format_marker(m, first=False) for m in marker) + if first: + return " ".join(inner) + else: + return "(" + " ".join(inner) + ")" + elif isinstance(marker, tuple): + return " ".join([m.serialize() for m in marker]) + else: + return marker + + +_operators = { + "in": lambda lhs, rhs: lhs in rhs, + "not in": lambda lhs, rhs: lhs not in rhs, + "<": operator.lt, + "<=": operator.le, + "==": operator.eq, + "!=": operator.ne, + ">=": operator.ge, + ">": operator.gt, +} + + +def _eval_op(lhs, op, rhs): + try: + spec = Specifier("".join([op.serialize(), rhs])) + except InvalidSpecifier: + pass + else: + return spec.contains(lhs) + + oper = _operators.get(op.serialize()) + if oper is None: + raise UndefinedComparison( + "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) + ) + + return oper(lhs, rhs) + + +_undefined = object() + + +def _get_env(environment, name): + value = environment.get(name, _undefined) + + if value is _undefined: + raise UndefinedEnvironmentName( + "{0!r} does not exist in evaluation environment.".format(name) + ) + + return value + + +def _evaluate_markers(markers, environment): + groups = [[]] + + for marker in markers: + assert isinstance(marker, (list, tuple, string_types)) + + if isinstance(marker, list): + groups[-1].append(_evaluate_markers(marker, environment)) + elif isinstance(marker, tuple): + lhs, op, rhs = marker + + if isinstance(lhs, Variable): + lhs_value = _get_env(environment, lhs.value) + rhs_value = rhs.value + else: + lhs_value = lhs.value + rhs_value = _get_env(environment, rhs.value) + + groups[-1].append(_eval_op(lhs_value, op, rhs_value)) + else: + assert marker in ["and", "or"] + if marker == "or": + groups.append([]) + + return any(all(item) for item in groups) + + +def format_full_version(info): + version = '{0.major}.{0.minor}.{0.micro}'.format(info) + kind = info.releaselevel + if kind != 'final': + version += kind[0] + str(info.serial) + return version + + +def default_environment(): + if hasattr(sys, 'implementation'): + iver = format_full_version(sys.implementation.version) + implementation_name = sys.implementation.name + else: + iver = '0' + implementation_name = '' + + return { + "implementation_name": implementation_name, + "implementation_version": iver, + "os_name": os.name, + "platform_machine": platform.machine(), + "platform_release": platform.release(), + "platform_system": platform.system(), + "platform_version": platform.version(), + "python_full_version": platform.python_version(), + "platform_python_implementation": platform.python_implementation(), + "python_version": platform.python_version()[:3], + "sys_platform": sys.platform, + } + + +class Marker(object): + + def __init__(self, marker): + try: + self._markers = _coerce_parse_result(MARKER.parseString(marker)) + except ParseException as e: + err_str = "Invalid marker: {0!r}, parse error at {1!r}".format( + marker, marker[e.loc:e.loc + 8]) + raise InvalidMarker(err_str) + + def __str__(self): + return _format_marker(self._markers) + + def __repr__(self): + return "<Marker({0!r})>".format(str(self)) + + def evaluate(self, environment=None): + """Evaluate a marker. + + Return the boolean from evaluating the given marker against the + environment. environment is an optional argument to override all or + part of the determined environment. + + The environment is determined from the current Python process. + """ + current_environment = default_environment() + if environment is not None: + current_environment.update(environment) + + return _evaluate_markers(self._markers, current_environment) diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/requirements.py b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/requirements.py new file mode 100644 index 0000000..0c8c4a3 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/requirements.py @@ -0,0 +1,127 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import string +import re + +from pkg_resources.extern.pyparsing import stringStart, stringEnd, originalTextFor, ParseException +from pkg_resources.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine +from pkg_resources.extern.pyparsing import Literal as L # noqa +from pkg_resources.extern.six.moves.urllib import parse as urlparse + +from .markers import MARKER_EXPR, Marker +from .specifiers import LegacySpecifier, Specifier, SpecifierSet + + +class InvalidRequirement(ValueError): + """ + An invalid requirement was found, users should refer to PEP 508. + """ + + +ALPHANUM = Word(string.ascii_letters + string.digits) + +LBRACKET = L("[").suppress() +RBRACKET = L("]").suppress() +LPAREN = L("(").suppress() +RPAREN = L(")").suppress() +COMMA = L(",").suppress() +SEMICOLON = L(";").suppress() +AT = L("@").suppress() + +PUNCTUATION = Word("-_.") +IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM) +IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) + +NAME = IDENTIFIER("name") +EXTRA = IDENTIFIER + +URI = Regex(r'[^ ]+')("url") +URL = (AT + URI) + +EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) +EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") + +VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) +VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) + +VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY +VERSION_MANY = Combine(VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), + joinString=",", adjacent=False)("_raw_spec") +_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)) +_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or '') + +VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") +VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) + +MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") +MARKER_EXPR.setParseAction( + lambda s, l, t: Marker(s[t._original_start:t._original_end]) +) +MARKER_SEPERATOR = SEMICOLON +MARKER = MARKER_SEPERATOR + MARKER_EXPR + +VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) +URL_AND_MARKER = URL + Optional(MARKER) + +NAMED_REQUIREMENT = \ + NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) + +REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd + + +class Requirement(object): + """Parse a requirement. + + Parse a given requirement string into its parts, such as name, specifier, + URL, and extras. Raises InvalidRequirement on a badly-formed requirement + string. + """ + + # TODO: Can we test whether something is contained within a requirement? + # If so how do we do that? Do we need to test against the _name_ of + # the thing as well as the version? What about the markers? + # TODO: Can we normalize the name and extra name? + + def __init__(self, requirement_string): + try: + req = REQUIREMENT.parseString(requirement_string) + except ParseException as e: + raise InvalidRequirement( + "Invalid requirement, parse error at \"{0!r}\"".format( + requirement_string[e.loc:e.loc + 8])) + + self.name = req.name + if req.url: + parsed_url = urlparse.urlparse(req.url) + if not (parsed_url.scheme and parsed_url.netloc) or ( + not parsed_url.scheme and not parsed_url.netloc): + raise InvalidRequirement("Invalid URL given") + self.url = req.url + else: + self.url = None + self.extras = set(req.extras.asList() if req.extras else []) + self.specifier = SpecifierSet(req.specifier) + self.marker = req.marker if req.marker else None + + def __str__(self): + parts = [self.name] + + if self.extras: + parts.append("[{0}]".format(",".join(sorted(self.extras)))) + + if self.specifier: + parts.append(str(self.specifier)) + + if self.url: + parts.append("@ {0}".format(self.url)) + + if self.marker: + parts.append("; {0}".format(self.marker)) + + return "".join(parts) + + def __repr__(self): + return "<Requirement({0!r})>".format(str(self)) diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/specifiers.py b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/specifiers.py new file mode 100644 index 0000000..7f5a76c --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/specifiers.py @@ -0,0 +1,774 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import abc +import functools +import itertools +import re + +from ._compat import string_types, with_metaclass +from .version import Version, LegacyVersion, parse + + +class InvalidSpecifier(ValueError): + """ + An invalid specifier was found, users should refer to PEP 440. + """ + + +class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): + + @abc.abstractmethod + def __str__(self): + """ + Returns the str representation of this Specifier like object. This + should be representative of the Specifier itself. + """ + + @abc.abstractmethod + def __hash__(self): + """ + Returns a hash value for this Specifier like object. + """ + + @abc.abstractmethod + def __eq__(self, other): + """ + Returns a boolean representing whether or not the two Specifier like + objects are equal. + """ + + @abc.abstractmethod + def __ne__(self, other): + """ + Returns a boolean representing whether or not the two Specifier like + objects are not equal. + """ + + @abc.abstractproperty + def prereleases(self): + """ + Returns whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @prereleases.setter + def prereleases(self, value): + """ + Sets whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @abc.abstractmethod + def contains(self, item, prereleases=None): + """ + Determines if the given item is contained within this specifier. + """ + + @abc.abstractmethod + def filter(self, iterable, prereleases=None): + """ + Takes an iterable of items and filters them so that only items which + are contained within this specifier are allowed in it. + """ + + +class _IndividualSpecifier(BaseSpecifier): + + _operators = {} + + def __init__(self, spec="", prereleases=None): + match = self._regex.search(spec) + if not match: + raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) + + self._spec = ( + match.group("operator").strip(), + match.group("version").strip(), + ) + + # Store whether or not this Specifier should accept prereleases + self._prereleases = prereleases + + def __repr__(self): + pre = ( + ", prereleases={0!r}".format(self.prereleases) + if self._prereleases is not None + else "" + ) + + return "<{0}({1!r}{2})>".format( + self.__class__.__name__, + str(self), + pre, + ) + + def __str__(self): + return "{0}{1}".format(*self._spec) + + def __hash__(self): + return hash(self._spec) + + def __eq__(self, other): + if isinstance(other, string_types): + try: + other = self.__class__(other) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._spec == other._spec + + def __ne__(self, other): + if isinstance(other, string_types): + try: + other = self.__class__(other) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._spec != other._spec + + def _get_operator(self, op): + return getattr(self, "_compare_{0}".format(self._operators[op])) + + def _coerce_version(self, version): + if not isinstance(version, (LegacyVersion, Version)): + version = parse(version) + return version + + @property + def operator(self): + return self._spec[0] + + @property + def version(self): + return self._spec[1] + + @property + def prereleases(self): + return self._prereleases + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + def __contains__(self, item): + return self.contains(item) + + def contains(self, item, prereleases=None): + # Determine if prereleases are to be allowed or not. + if prereleases is None: + prereleases = self.prereleases + + # Normalize item to a Version or LegacyVersion, this allows us to have + # a shortcut for ``"2.0" in Specifier(">=2") + item = self._coerce_version(item) + + # Determine if we should be supporting prereleases in this specifier + # or not, if we do not support prereleases than we can short circuit + # logic if this version is a prereleases. + if item.is_prerelease and not prereleases: + return False + + # Actually do the comparison to determine if this item is contained + # within this Specifier or not. + return self._get_operator(self.operator)(item, self.version) + + def filter(self, iterable, prereleases=None): + yielded = False + found_prereleases = [] + + kw = {"prereleases": prereleases if prereleases is not None else True} + + # Attempt to iterate over all the values in the iterable and if any of + # them match, yield them. + for version in iterable: + parsed_version = self._coerce_version(version) + + if self.contains(parsed_version, **kw): + # If our version is a prerelease, and we were not set to allow + # prereleases, then we'll store it for later incase nothing + # else matches this specifier. + if (parsed_version.is_prerelease and not + (prereleases or self.prereleases)): + found_prereleases.append(version) + # Either this is not a prerelease, or we should have been + # accepting prereleases from the begining. + else: + yielded = True + yield version + + # Now that we've iterated over everything, determine if we've yielded + # any values, and if we have not and we have any prereleases stored up + # then we will go ahead and yield the prereleases. + if not yielded and found_prereleases: + for version in found_prereleases: + yield version + + +class LegacySpecifier(_IndividualSpecifier): + + _regex_str = ( + r""" + (?P<operator>(==|!=|<=|>=|<|>)) + \s* + (?P<version> + [^,;\s)]* # Since this is a "legacy" specifier, and the version + # string can be just about anything, we match everything + # except for whitespace, a semi-colon for marker support, + # a closing paren since versions can be enclosed in + # them, and a comma since it's a version separator. + ) + """ + ) + + _regex = re.compile( + r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + + _operators = { + "==": "equal", + "!=": "not_equal", + "<=": "less_than_equal", + ">=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + } + + def _coerce_version(self, version): + if not isinstance(version, LegacyVersion): + version = LegacyVersion(str(version)) + return version + + def _compare_equal(self, prospective, spec): + return prospective == self._coerce_version(spec) + + def _compare_not_equal(self, prospective, spec): + return prospective != self._coerce_version(spec) + + def _compare_less_than_equal(self, prospective, spec): + return prospective <= self._coerce_version(spec) + + def _compare_greater_than_equal(self, prospective, spec): + return prospective >= self._coerce_version(spec) + + def _compare_less_than(self, prospective, spec): + return prospective < self._coerce_version(spec) + + def _compare_greater_than(self, prospective, spec): + return prospective > self._coerce_version(spec) + + +def _require_version_compare(fn): + @functools.wraps(fn) + def wrapped(self, prospective, spec): + if not isinstance(prospective, Version): + return False + return fn(self, prospective, spec) + return wrapped + + +class Specifier(_IndividualSpecifier): + + _regex_str = ( + r""" + (?P<operator>(~=|==|!=|<=|>=|<|>|===)) + (?P<version> + (?: + # The identity operators allow for an escape hatch that will + # do an exact string match of the version you wish to install. + # This will not be parsed by PEP 440 and we cannot determine + # any semantic meaning from it. This operator is discouraged + # but included entirely as an escape hatch. + (?<====) # Only match for the identity operator + \s* + [^\s]* # We just match everything, except for whitespace + # since we are only testing for strict identity. + ) + | + (?: + # The (non)equality operators allow for wild card and local + # versions to be specified so we have to define these two + # operators separately to enable that. + (?<===|!=) # Only match for equals and not equals + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + + # You cannot use a wild card and a dev or local version + # together so group them with a | and make them optional. + (?: + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local + | + \.\* # Wild card syntax of .* + )? + ) + | + (?: + # The compatible operator requires at least two digits in the + # release segment. + (?<=~=) # Only match for the compatible operator + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + | + (?: + # All other operators only allow a sub set of what the + # (non)equality operators do. Specifically they do not allow + # local versions to be specified nor do they allow the prefix + # matching wild cards. + (?<!==|!=|~=) # We have special cases for these + # operators so we want to make sure they + # don't match here. + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + ) + """ + ) + + _regex = re.compile( + r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + + _operators = { + "~=": "compatible", + "==": "equal", + "!=": "not_equal", + "<=": "less_than_equal", + ">=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + "===": "arbitrary", + } + + @_require_version_compare + def _compare_compatible(self, prospective, spec): + # Compatible releases have an equivalent combination of >= and ==. That + # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to + # implement this in terms of the other specifiers instead of + # implementing it ourselves. The only thing we need to do is construct + # the other specifiers. + + # We want everything but the last item in the version, but we want to + # ignore post and dev releases and we want to treat the pre-release as + # it's own separate segment. + prefix = ".".join( + list( + itertools.takewhile( + lambda x: (not x.startswith("post") and not + x.startswith("dev")), + _version_split(spec), + ) + )[:-1] + ) + + # Add the prefix notation to the end of our string + prefix += ".*" + + return (self._get_operator(">=")(prospective, spec) and + self._get_operator("==")(prospective, prefix)) + + @_require_version_compare + def _compare_equal(self, prospective, spec): + # We need special logic to handle prefix matching + if spec.endswith(".*"): + # In the case of prefix matching we want to ignore local segment. + prospective = Version(prospective.public) + # Split the spec out by dots, and pretend that there is an implicit + # dot in between a release segment and a pre-release segment. + spec = _version_split(spec[:-2]) # Remove the trailing .* + + # Split the prospective version out by dots, and pretend that there + # is an implicit dot in between a release segment and a pre-release + # segment. + prospective = _version_split(str(prospective)) + + # Shorten the prospective version to be the same length as the spec + # so that we can determine if the specifier is a prefix of the + # prospective version or not. + prospective = prospective[:len(spec)] + + # Pad out our two sides with zeros so that they both equal the same + # length. + spec, prospective = _pad_version(spec, prospective) + else: + # Convert our spec string into a Version + spec = Version(spec) + + # If the specifier does not have a local segment, then we want to + # act as if the prospective version also does not have a local + # segment. + if not spec.local: + prospective = Version(prospective.public) + + return prospective == spec + + @_require_version_compare + def _compare_not_equal(self, prospective, spec): + return not self._compare_equal(prospective, spec) + + @_require_version_compare + def _compare_less_than_equal(self, prospective, spec): + return prospective <= Version(spec) + + @_require_version_compare + def _compare_greater_than_equal(self, prospective, spec): + return prospective >= Version(spec) + + @_require_version_compare + def _compare_less_than(self, prospective, spec): + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec) + + # Check to see if the prospective version is less than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective < spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a pre-release version, that we do not accept pre-release + # versions for the version mentioned in the specifier (e.g. <3.1 should + # not match 3.1.dev0, but should match 3.0.dev0). + if not spec.is_prerelease and prospective.is_prerelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # less than the spec version *and* it's not a pre-release of the same + # version in the spec. + return True + + @_require_version_compare + def _compare_greater_than(self, prospective, spec): + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec) + + # Check to see if the prospective version is greater than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective > spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a post-release version, that we do not accept + # post-release versions for the version mentioned in the specifier + # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). + if not spec.is_postrelease and prospective.is_postrelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # Ensure that we do not allow a local version of the version mentioned + # in the specifier, which is techincally greater than, to match. + if prospective.local is not None: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # greater than the spec version *and* it's not a pre-release of the + # same version in the spec. + return True + + def _compare_arbitrary(self, prospective, spec): + return str(prospective).lower() == str(spec).lower() + + @property + def prereleases(self): + # If there is an explicit prereleases set for this, then we'll just + # blindly use that. + if self._prereleases is not None: + return self._prereleases + + # Look at all of our specifiers and determine if they are inclusive + # operators, and if they are if they are including an explicit + # prerelease. + operator, version = self._spec + if operator in ["==", ">=", "<=", "~=", "==="]: + # The == specifier can include a trailing .*, if it does we + # want to remove before parsing. + if operator == "==" and version.endswith(".*"): + version = version[:-2] + + # Parse the version, and if it is a pre-release than this + # specifier allows pre-releases. + if parse(version).is_prerelease: + return True + + return False + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + +_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") + + +def _version_split(version): + result = [] + for item in version.split("."): + match = _prefix_regex.search(item) + if match: + result.extend(match.groups()) + else: + result.append(item) + return result + + +def _pad_version(left, right): + left_split, right_split = [], [] + + # Get the release segment of our versions + left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) + right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) + + # Get the rest of our versions + left_split.append(left[len(left_split[0]):]) + right_split.append(right[len(right_split[0]):]) + + # Insert our padding + left_split.insert( + 1, + ["0"] * max(0, len(right_split[0]) - len(left_split[0])), + ) + right_split.insert( + 1, + ["0"] * max(0, len(left_split[0]) - len(right_split[0])), + ) + + return ( + list(itertools.chain(*left_split)), + list(itertools.chain(*right_split)), + ) + + +class SpecifierSet(BaseSpecifier): + + def __init__(self, specifiers="", prereleases=None): + # Split on , to break each indidivual specifier into it's own item, and + # strip each item to remove leading/trailing whitespace. + specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + + # Parsed each individual specifier, attempting first to make it a + # Specifier and falling back to a LegacySpecifier. + parsed = set() + for specifier in specifiers: + try: + parsed.add(Specifier(specifier)) + except InvalidSpecifier: + parsed.add(LegacySpecifier(specifier)) + + # Turn our parsed specifiers into a frozen set and save them for later. + self._specs = frozenset(parsed) + + # Store our prereleases value so we can use it later to determine if + # we accept prereleases or not. + self._prereleases = prereleases + + def __repr__(self): + pre = ( + ", prereleases={0!r}".format(self.prereleases) + if self._prereleases is not None + else "" + ) + + return "<SpecifierSet({0!r}{1})>".format(str(self), pre) + + def __str__(self): + return ",".join(sorted(str(s) for s in self._specs)) + + def __hash__(self): + return hash(self._specs) + + def __and__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + specifier = SpecifierSet() + specifier._specs = frozenset(self._specs | other._specs) + + if self._prereleases is None and other._prereleases is not None: + specifier._prereleases = other._prereleases + elif self._prereleases is not None and other._prereleases is None: + specifier._prereleases = self._prereleases + elif self._prereleases == other._prereleases: + specifier._prereleases = self._prereleases + else: + raise ValueError( + "Cannot combine SpecifierSets with True and False prerelease " + "overrides." + ) + + return specifier + + def __eq__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif isinstance(other, _IndividualSpecifier): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs == other._specs + + def __ne__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif isinstance(other, _IndividualSpecifier): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs != other._specs + + def __len__(self): + return len(self._specs) + + def __iter__(self): + return iter(self._specs) + + @property + def prereleases(self): + # If we have been given an explicit prerelease modifier, then we'll + # pass that through here. + if self._prereleases is not None: + return self._prereleases + + # If we don't have any specifiers, and we don't have a forced value, + # then we'll just return None since we don't know if this should have + # pre-releases or not. + if not self._specs: + return None + + # Otherwise we'll see if any of the given specifiers accept + # prereleases, if any of them do we'll return True, otherwise False. + return any(s.prereleases for s in self._specs) + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + def __contains__(self, item): + return self.contains(item) + + def contains(self, item, prereleases=None): + # Ensure that our item is a Version or LegacyVersion instance. + if not isinstance(item, (LegacyVersion, Version)): + item = parse(item) + + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # We can determine if we're going to allow pre-releases by looking to + # see if any of the underlying items supports them. If none of them do + # and this item is a pre-release then we do not allow it and we can + # short circuit that here. + # Note: This means that 1.0.dev1 would not be contained in something + # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 + if not prereleases and item.is_prerelease: + return False + + # We simply dispatch to the underlying specs here to make sure that the + # given version is contained within all of them. + # Note: This use of all() here means that an empty set of specifiers + # will always return True, this is an explicit design decision. + return all( + s.contains(item, prereleases=prereleases) + for s in self._specs + ) + + def filter(self, iterable, prereleases=None): + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # If we have any specifiers, then we want to wrap our iterable in the + # filter method for each one, this will act as a logical AND amongst + # each specifier. + if self._specs: + for spec in self._specs: + iterable = spec.filter(iterable, prereleases=bool(prereleases)) + return iterable + # If we do not have any specifiers, then we need to have a rough filter + # which will filter out any pre-releases, unless there are no final + # releases, and which will filter out LegacyVersion in general. + else: + filtered = [] + found_prereleases = [] + + for item in iterable: + # Ensure that we some kind of Version class for this item. + if not isinstance(item, (LegacyVersion, Version)): + parsed_version = parse(item) + else: + parsed_version = item + + # Filter out any item which is parsed as a LegacyVersion + if isinstance(parsed_version, LegacyVersion): + continue + + # Store any item which is a pre-release for later unless we've + # already found a final version or we are accepting prereleases + if parsed_version.is_prerelease and not prereleases: + if not filtered: + found_prereleases.append(item) + else: + filtered.append(item) + + # If we've found no items except for pre-releases, then we'll go + # ahead and use the pre-releases + if not filtered and found_prereleases and prereleases is None: + return found_prereleases + + return filtered diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/utils.py b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/utils.py new file mode 100644 index 0000000..942387c --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/utils.py @@ -0,0 +1,14 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import re + + +_canonicalize_regex = re.compile(r"[-_.]+") + + +def canonicalize_name(name): + # This is taken from PEP 503. + return _canonicalize_regex.sub("-", name).lower() diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/version.py b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/version.py new file mode 100644 index 0000000..83b5ee8 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/version.py @@ -0,0 +1,393 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import collections +import itertools +import re + +from ._structures import Infinity + + +__all__ = [ + "parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN" +] + + +_Version = collections.namedtuple( + "_Version", + ["epoch", "release", "dev", "pre", "post", "local"], +) + + +def parse(version): + """ + Parse the given version string and return either a :class:`Version` object + or a :class:`LegacyVersion` object depending on if the given version is + a valid PEP 440 version or a legacy version. + """ + try: + return Version(version) + except InvalidVersion: + return LegacyVersion(version) + + +class InvalidVersion(ValueError): + """ + An invalid version was found, users should refer to PEP 440. + """ + + +class _BaseVersion(object): + + def __hash__(self): + return hash(self._key) + + def __lt__(self, other): + return self._compare(other, lambda s, o: s < o) + + def __le__(self, other): + return self._compare(other, lambda s, o: s <= o) + + def __eq__(self, other): + return self._compare(other, lambda s, o: s == o) + + def __ge__(self, other): + return self._compare(other, lambda s, o: s >= o) + + def __gt__(self, other): + return self._compare(other, lambda s, o: s > o) + + def __ne__(self, other): + return self._compare(other, lambda s, o: s != o) + + def _compare(self, other, method): + if not isinstance(other, _BaseVersion): + return NotImplemented + + return method(self._key, other._key) + + +class LegacyVersion(_BaseVersion): + + def __init__(self, version): + self._version = str(version) + self._key = _legacy_cmpkey(self._version) + + def __str__(self): + return self._version + + def __repr__(self): + return "<LegacyVersion({0})>".format(repr(str(self))) + + @property + def public(self): + return self._version + + @property + def base_version(self): + return self._version + + @property + def local(self): + return None + + @property + def is_prerelease(self): + return False + + @property + def is_postrelease(self): + return False + + +_legacy_version_component_re = re.compile( + r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE, +) + +_legacy_version_replacement_map = { + "pre": "c", "preview": "c", "-": "final-", "rc": "c", "dev": "@", +} + + +def _parse_version_parts(s): + for part in _legacy_version_component_re.split(s): + part = _legacy_version_replacement_map.get(part, part) + + if not part or part == ".": + continue + + if part[:1] in "0123456789": + # pad for numeric comparison + yield part.zfill(8) + else: + yield "*" + part + + # ensure that alpha/beta/candidate are before final + yield "*final" + + +def _legacy_cmpkey(version): + # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch + # greater than or equal to 0. This will effectively put the LegacyVersion, + # which uses the defacto standard originally implemented by setuptools, + # as before all PEP 440 versions. + epoch = -1 + + # This scheme is taken from pkg_resources.parse_version setuptools prior to + # it's adoption of the packaging library. + parts = [] + for part in _parse_version_parts(version.lower()): + if part.startswith("*"): + # remove "-" before a prerelease tag + if part < "*final": + while parts and parts[-1] == "*final-": + parts.pop() + + # remove trailing zeros from each series of numeric parts + while parts and parts[-1] == "00000000": + parts.pop() + + parts.append(part) + parts = tuple(parts) + + return epoch, parts + +# Deliberately not anchored to the start and end of the string, to make it +# easier for 3rd party code to reuse +VERSION_PATTERN = r""" + v? + (?: + (?:(?P<epoch>[0-9]+)!)? # epoch + (?P<release>[0-9]+(?:\.[0-9]+)*) # release segment + (?P<pre> # pre-release + [-_\.]? + (?P<pre_l>(a|b|c|rc|alpha|beta|pre|preview)) + [-_\.]? + (?P<pre_n>[0-9]+)? + )? + (?P<post> # post release + (?:-(?P<post_n1>[0-9]+)) + | + (?: + [-_\.]? + (?P<post_l>post|rev|r) + [-_\.]? + (?P<post_n2>[0-9]+)? + ) + )? + (?P<dev> # dev release + [-_\.]? + (?P<dev_l>dev) + [-_\.]? + (?P<dev_n>[0-9]+)? + )? + ) + (?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version +""" + + +class Version(_BaseVersion): + + _regex = re.compile( + r"^\s*" + VERSION_PATTERN + r"\s*$", + re.VERBOSE | re.IGNORECASE, + ) + + def __init__(self, version): + # Validate the version and parse it into pieces + match = self._regex.search(version) + if not match: + raise InvalidVersion("Invalid version: '{0}'".format(version)) + + # Store the parsed out pieces of the version + self._version = _Version( + epoch=int(match.group("epoch")) if match.group("epoch") else 0, + release=tuple(int(i) for i in match.group("release").split(".")), + pre=_parse_letter_version( + match.group("pre_l"), + match.group("pre_n"), + ), + post=_parse_letter_version( + match.group("post_l"), + match.group("post_n1") or match.group("post_n2"), + ), + dev=_parse_letter_version( + match.group("dev_l"), + match.group("dev_n"), + ), + local=_parse_local_version(match.group("local")), + ) + + # Generate a key which will be used for sorting + self._key = _cmpkey( + self._version.epoch, + self._version.release, + self._version.pre, + self._version.post, + self._version.dev, + self._version.local, + ) + + def __repr__(self): + return "<Version({0})>".format(repr(str(self))) + + def __str__(self): + parts = [] + + # Epoch + if self._version.epoch != 0: + parts.append("{0}!".format(self._version.epoch)) + + # Release segment + parts.append(".".join(str(x) for x in self._version.release)) + + # Pre-release + if self._version.pre is not None: + parts.append("".join(str(x) for x in self._version.pre)) + + # Post-release + if self._version.post is not None: + parts.append(".post{0}".format(self._version.post[1])) + + # Development release + if self._version.dev is not None: + parts.append(".dev{0}".format(self._version.dev[1])) + + # Local version segment + if self._version.local is not None: + parts.append( + "+{0}".format(".".join(str(x) for x in self._version.local)) + ) + + return "".join(parts) + + @property + def public(self): + return str(self).split("+", 1)[0] + + @property + def base_version(self): + parts = [] + + # Epoch + if self._version.epoch != 0: + parts.append("{0}!".format(self._version.epoch)) + + # Release segment + parts.append(".".join(str(x) for x in self._version.release)) + + return "".join(parts) + + @property + def local(self): + version_string = str(self) + if "+" in version_string: + return version_string.split("+", 1)[1] + + @property + def is_prerelease(self): + return bool(self._version.dev or self._version.pre) + + @property + def is_postrelease(self): + return bool(self._version.post) + + +def _parse_letter_version(letter, number): + if letter: + # We consider there to be an implicit 0 in a pre-release if there is + # not a numeral associated with it. + if number is None: + number = 0 + + # We normalize any letters to their lower case form + letter = letter.lower() + + # We consider some words to be alternate spellings of other words and + # in those cases we want to normalize the spellings to our preferred + # spelling. + if letter == "alpha": + letter = "a" + elif letter == "beta": + letter = "b" + elif letter in ["c", "pre", "preview"]: + letter = "rc" + elif letter in ["rev", "r"]: + letter = "post" + + return letter, int(number) + if not letter and number: + # We assume if we are given a number, but we are not given a letter + # then this is using the implicit post release syntax (e.g. 1.0-1) + letter = "post" + + return letter, int(number) + + +_local_version_seperators = re.compile(r"[\._-]") + + +def _parse_local_version(local): + """ + Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve"). + """ + if local is not None: + return tuple( + part.lower() if not part.isdigit() else int(part) + for part in _local_version_seperators.split(local) + ) + + +def _cmpkey(epoch, release, pre, post, dev, local): + # When we compare a release version, we want to compare it with all of the + # trailing zeros removed. So we'll use a reverse the list, drop all the now + # leading zeros until we come to something non zero, then take the rest + # re-reverse it back into the correct order and make it a tuple and use + # that for our sorting key. + release = tuple( + reversed(list( + itertools.dropwhile( + lambda x: x == 0, + reversed(release), + ) + )) + ) + + # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0. + # We'll do this by abusing the pre segment, but we _only_ want to do this + # if there is not a pre or a post segment. If we have one of those then + # the normal sorting rules will handle this case correctly. + if pre is None and post is None and dev is not None: + pre = -Infinity + # Versions without a pre-release (except as noted above) should sort after + # those with one. + elif pre is None: + pre = Infinity + + # Versions without a post segment should sort before those with one. + if post is None: + post = -Infinity + + # Versions without a development segment should sort after those with one. + if dev is None: + dev = Infinity + + if local is None: + # Versions without a local segment should sort before those with one. + local = -Infinity + else: + # Versions with a local segment need that segment parsed to implement + # the sorting rules in PEP440. + # - Alpha numeric segments sort before numeric segments + # - Alpha numeric segments sort lexicographically + # - Numeric segments sort numerically + # - Shorter versions sort before longer versions when the prefixes + # match exactly + local = tuple( + (i, "") if isinstance(i, int) else (-Infinity, i) + for i in local + ) + + return epoch, release, pre, post, dev, local diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/pyparsing.py b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/pyparsing.py new file mode 100644 index 0000000..cf75e1e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/pyparsing.py @@ -0,0 +1,5742 @@ +# module pyparsing.py +# +# Copyright (c) 2003-2018 Paul T. McGuire +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__doc__ = \ +""" +pyparsing module - Classes and methods to define and execute parsing grammars +============================================================================= + +The pyparsing module is an alternative approach to creating and executing simple grammars, +vs. the traditional lex/yacc approach, or the use of regular expressions. With pyparsing, you +don't need to learn a new syntax for defining grammars or matching expressions - the parsing module +provides a library of classes that you use to construct the grammar directly in Python. + +Here is a program to parse "Hello, World!" (or any greeting of the form +C{"<salutation>, <addressee>!"}), built up using L{Word}, L{Literal}, and L{And} elements +(L{'+'<ParserElement.__add__>} operator gives L{And} expressions, strings are auto-converted to +L{Literal} expressions):: + + from pyparsing import Word, alphas + + # define grammar of a greeting + greet = Word(alphas) + "," + Word(alphas) + "!" + + hello = "Hello, World!" + print (hello, "->", greet.parseString(hello)) + +The program outputs the following:: + + Hello, World! -> ['Hello', ',', 'World', '!'] + +The Python representation of the grammar is quite readable, owing to the self-explanatory +class names, and the use of '+', '|' and '^' operators. + +The L{ParseResults} object returned from L{ParserElement.parseString<ParserElement.parseString>} can be accessed as a nested list, a dictionary, or an +object with named attributes. + +The pyparsing module handles some of the problems that are typically vexing when writing text parsers: + - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello , World !", etc.) + - quoted strings + - embedded comments + + +Getting Started - +----------------- +Visit the classes L{ParserElement} and L{ParseResults} to see the base classes that most other pyparsing +classes inherit from. Use the docstrings for examples of how to: + - construct literal match expressions from L{Literal} and L{CaselessLiteral} classes + - construct character word-group expressions using the L{Word} class + - see how to create repetitive expressions using L{ZeroOrMore} and L{OneOrMore} classes + - use L{'+'<And>}, L{'|'<MatchFirst>}, L{'^'<Or>}, and L{'&'<Each>} operators to combine simple expressions into more complex ones + - associate names with your parsed results using L{ParserElement.setResultsName} + - find some helpful expression short-cuts like L{delimitedList} and L{oneOf} + - find more useful common expressions in the L{pyparsing_common} namespace class +""" + +__version__ = "2.2.1" +__versionTime__ = "18 Sep 2018 00:49 UTC" +__author__ = "Paul McGuire <ptmcg@users.sourceforge.net>" + +import string +from weakref import ref as wkref +import copy +import sys +import warnings +import re +import sre_constants +import collections +import pprint +import traceback +import types +from datetime import datetime + +try: + from _thread import RLock +except ImportError: + from threading import RLock + +try: + # Python 3 + from collections.abc import Iterable + from collections.abc import MutableMapping +except ImportError: + # Python 2.7 + from collections import Iterable + from collections import MutableMapping + +try: + from collections import OrderedDict as _OrderedDict +except ImportError: + try: + from ordereddict import OrderedDict as _OrderedDict + except ImportError: + _OrderedDict = None + +#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) ) + +__all__ = [ +'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty', +'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal', +'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or', +'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException', +'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException', +'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', +'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore', +'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col', +'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString', +'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums', +'htmlComment', 'javaStyleComment', 'line', 'lineEnd', 'lineStart', 'lineno', +'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral', +'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables', +'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', +'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd', +'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute', +'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation','locatedExpr', 'withClass', +'CloseMatch', 'tokenMap', 'pyparsing_common', +] + +system_version = tuple(sys.version_info)[:3] +PY_3 = system_version[0] == 3 +if PY_3: + _MAX_INT = sys.maxsize + basestring = str + unichr = chr + _ustr = str + + # build list of single arg builtins, that can be used as parse actions + singleArgBuiltins = [sum, len, sorted, reversed, list, tuple, set, any, all, min, max] + +else: + _MAX_INT = sys.maxint + range = xrange + + def _ustr(obj): + """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries + str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It + then < returns the unicode object | encodes it with the default encoding | ... >. + """ + if isinstance(obj,unicode): + return obj + + try: + # If this works, then _ustr(obj) has the same behaviour as str(obj), so + # it won't break any existing code. + return str(obj) + + except UnicodeEncodeError: + # Else encode it + ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace') + xmlcharref = Regex(r'&#\d+;') + xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:]) + return xmlcharref.transformString(ret) + + # build list of single arg builtins, tolerant of Python version, that can be used as parse actions + singleArgBuiltins = [] + import __builtin__ + for fname in "sum len sorted reversed list tuple set any all min max".split(): + try: + singleArgBuiltins.append(getattr(__builtin__,fname)) + except AttributeError: + continue + +_generatorType = type((y for y in range(1))) + +def _xml_escape(data): + """Escape &, <, >, ", ', etc. in a string of data.""" + + # ampersand must be replaced first + from_symbols = '&><"\'' + to_symbols = ('&'+s+';' for s in "amp gt lt quot apos".split()) + for from_,to_ in zip(from_symbols, to_symbols): + data = data.replace(from_, to_) + return data + +class _Constants(object): + pass + +alphas = string.ascii_uppercase + string.ascii_lowercase +nums = "0123456789" +hexnums = nums + "ABCDEFabcdef" +alphanums = alphas + nums +_bslash = chr(92) +printables = "".join(c for c in string.printable if c not in string.whitespace) + +class ParseBaseException(Exception): + """base exception class for all parsing runtime exceptions""" + # Performance tuning: we construct a *lot* of these, so keep this + # constructor as small and fast as possible + def __init__( self, pstr, loc=0, msg=None, elem=None ): + self.loc = loc + if msg is None: + self.msg = pstr + self.pstr = "" + else: + self.msg = msg + self.pstr = pstr + self.parserElement = elem + self.args = (pstr, loc, msg) + + @classmethod + def _from_exception(cls, pe): + """ + internal factory method to simplify creating one type of ParseException + from another - avoids having __init__ signature conflicts among subclasses + """ + return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement) + + def __getattr__( self, aname ): + """supported attributes by name are: + - lineno - returns the line number of the exception text + - col - returns the column number of the exception text + - line - returns the line containing the exception text + """ + if( aname == "lineno" ): + return lineno( self.loc, self.pstr ) + elif( aname in ("col", "column") ): + return col( self.loc, self.pstr ) + elif( aname == "line" ): + return line( self.loc, self.pstr ) + else: + raise AttributeError(aname) + + def __str__( self ): + return "%s (at char %d), (line:%d, col:%d)" % \ + ( self.msg, self.loc, self.lineno, self.column ) + def __repr__( self ): + return _ustr(self) + def markInputline( self, markerString = ">!<" ): + """Extracts the exception line from the input string, and marks + the location of the exception with a special symbol. + """ + line_str = self.line + line_column = self.column - 1 + if markerString: + line_str = "".join((line_str[:line_column], + markerString, line_str[line_column:])) + return line_str.strip() + def __dir__(self): + return "lineno col line".split() + dir(type(self)) + +class ParseException(ParseBaseException): + """ + Exception thrown when parse expressions don't match class; + supported attributes by name are: + - lineno - returns the line number of the exception text + - col - returns the column number of the exception text + - line - returns the line containing the exception text + + Example:: + try: + Word(nums).setName("integer").parseString("ABC") + except ParseException as pe: + print(pe) + print("column: {}".format(pe.col)) + + prints:: + Expected integer (at char 0), (line:1, col:1) + column: 1 + """ + pass + +class ParseFatalException(ParseBaseException): + """user-throwable exception thrown when inconsistent parse content + is found; stops all parsing immediately""" + pass + +class ParseSyntaxException(ParseFatalException): + """just like L{ParseFatalException}, but thrown internally when an + L{ErrorStop<And._ErrorStop>} ('-' operator) indicates that parsing is to stop + immediately because an unbacktrackable syntax error has been found""" + pass + +#~ class ReparseException(ParseBaseException): + #~ """Experimental class - parse actions can raise this exception to cause + #~ pyparsing to reparse the input string: + #~ - with a modified input string, and/or + #~ - with a modified start location + #~ Set the values of the ReparseException in the constructor, and raise the + #~ exception in a parse action to cause pyparsing to use the new string/location. + #~ Setting the values as None causes no change to be made. + #~ """ + #~ def __init_( self, newstring, restartLoc ): + #~ self.newParseText = newstring + #~ self.reparseLoc = restartLoc + +class RecursiveGrammarException(Exception): + """exception thrown by L{ParserElement.validate} if the grammar could be improperly recursive""" + def __init__( self, parseElementList ): + self.parseElementTrace = parseElementList + + def __str__( self ): + return "RecursiveGrammarException: %s" % self.parseElementTrace + +class _ParseResultsWithOffset(object): + def __init__(self,p1,p2): + self.tup = (p1,p2) + def __getitem__(self,i): + return self.tup[i] + def __repr__(self): + return repr(self.tup[0]) + def setOffset(self,i): + self.tup = (self.tup[0],i) + +class ParseResults(object): + """ + Structured parse results, to provide multiple means of access to the parsed data: + - as a list (C{len(results)}) + - by list index (C{results[0], results[1]}, etc.) + - by attribute (C{results.<resultsName>} - see L{ParserElement.setResultsName}) + + Example:: + integer = Word(nums) + date_str = (integer.setResultsName("year") + '/' + + integer.setResultsName("month") + '/' + + integer.setResultsName("day")) + # equivalent form: + # date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + # parseString returns a ParseResults object + result = date_str.parseString("1999/12/31") + + def test(s, fn=repr): + print("%s -> %s" % (s, fn(eval(s)))) + test("list(result)") + test("result[0]") + test("result['month']") + test("result.day") + test("'month' in result") + test("'minutes' in result") + test("result.dump()", str) + prints:: + list(result) -> ['1999', '/', '12', '/', '31'] + result[0] -> '1999' + result['month'] -> '12' + result.day -> '31' + 'month' in result -> True + 'minutes' in result -> False + result.dump() -> ['1999', '/', '12', '/', '31'] + - day: 31 + - month: 12 + - year: 1999 + """ + def __new__(cls, toklist=None, name=None, asList=True, modal=True ): + if isinstance(toklist, cls): + return toklist + retobj = object.__new__(cls) + retobj.__doinit = True + return retobj + + # Performance tuning: we construct a *lot* of these, so keep this + # constructor as small and fast as possible + def __init__( self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance ): + if self.__doinit: + self.__doinit = False + self.__name = None + self.__parent = None + self.__accumNames = {} + self.__asList = asList + self.__modal = modal + if toklist is None: + toklist = [] + if isinstance(toklist, list): + self.__toklist = toklist[:] + elif isinstance(toklist, _generatorType): + self.__toklist = list(toklist) + else: + self.__toklist = [toklist] + self.__tokdict = dict() + + if name is not None and name: + if not modal: + self.__accumNames[name] = 0 + if isinstance(name,int): + name = _ustr(name) # will always return a str, but use _ustr for consistency + self.__name = name + if not (isinstance(toklist, (type(None), basestring, list)) and toklist in (None,'',[])): + if isinstance(toklist,basestring): + toklist = [ toklist ] + if asList: + if isinstance(toklist,ParseResults): + self[name] = _ParseResultsWithOffset(toklist.copy(),0) + else: + self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0) + self[name].__name = name + else: + try: + self[name] = toklist[0] + except (KeyError,TypeError,IndexError): + self[name] = toklist + + def __getitem__( self, i ): + if isinstance( i, (int,slice) ): + return self.__toklist[i] + else: + if i not in self.__accumNames: + return self.__tokdict[i][-1][0] + else: + return ParseResults([ v[0] for v in self.__tokdict[i] ]) + + def __setitem__( self, k, v, isinstance=isinstance ): + if isinstance(v,_ParseResultsWithOffset): + self.__tokdict[k] = self.__tokdict.get(k,list()) + [v] + sub = v[0] + elif isinstance(k,(int,slice)): + self.__toklist[k] = v + sub = v + else: + self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)] + sub = v + if isinstance(sub,ParseResults): + sub.__parent = wkref(self) + + def __delitem__( self, i ): + if isinstance(i,(int,slice)): + mylen = len( self.__toklist ) + del self.__toklist[i] + + # convert int to slice + if isinstance(i, int): + if i < 0: + i += mylen + i = slice(i, i+1) + # get removed indices + removed = list(range(*i.indices(mylen))) + removed.reverse() + # fixup indices in token dictionary + for name,occurrences in self.__tokdict.items(): + for j in removed: + for k, (value, position) in enumerate(occurrences): + occurrences[k] = _ParseResultsWithOffset(value, position - (position > j)) + else: + del self.__tokdict[i] + + def __contains__( self, k ): + return k in self.__tokdict + + def __len__( self ): return len( self.__toklist ) + def __bool__(self): return ( not not self.__toklist ) + __nonzero__ = __bool__ + def __iter__( self ): return iter( self.__toklist ) + def __reversed__( self ): return iter( self.__toklist[::-1] ) + def _iterkeys( self ): + if hasattr(self.__tokdict, "iterkeys"): + return self.__tokdict.iterkeys() + else: + return iter(self.__tokdict) + + def _itervalues( self ): + return (self[k] for k in self._iterkeys()) + + def _iteritems( self ): + return ((k, self[k]) for k in self._iterkeys()) + + if PY_3: + keys = _iterkeys + """Returns an iterator of all named result keys (Python 3.x only).""" + + values = _itervalues + """Returns an iterator of all named result values (Python 3.x only).""" + + items = _iteritems + """Returns an iterator of all named result key-value tuples (Python 3.x only).""" + + else: + iterkeys = _iterkeys + """Returns an iterator of all named result keys (Python 2.x only).""" + + itervalues = _itervalues + """Returns an iterator of all named result values (Python 2.x only).""" + + iteritems = _iteritems + """Returns an iterator of all named result key-value tuples (Python 2.x only).""" + + def keys( self ): + """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x).""" + return list(self.iterkeys()) + + def values( self ): + """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x).""" + return list(self.itervalues()) + + def items( self ): + """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x).""" + return list(self.iteritems()) + + def haskeys( self ): + """Since keys() returns an iterator, this method is helpful in bypassing + code that looks for the existence of any defined results names.""" + return bool(self.__tokdict) + + def pop( self, *args, **kwargs): + """ + Removes and returns item at specified index (default=C{last}). + Supports both C{list} and C{dict} semantics for C{pop()}. If passed no + argument or an integer argument, it will use C{list} semantics + and pop tokens from the list of parsed tokens. If passed a + non-integer argument (most likely a string), it will use C{dict} + semantics and pop the corresponding value from any defined + results names. A second default return value argument is + supported, just as in C{dict.pop()}. + + Example:: + def remove_first(tokens): + tokens.pop(0) + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321'] + + label = Word(alphas) + patt = label("LABEL") + OneOrMore(Word(nums)) + print(patt.parseString("AAB 123 321").dump()) + + # Use pop() in a parse action to remove named result (note that corresponding value is not + # removed from list form of results) + def remove_LABEL(tokens): + tokens.pop("LABEL") + return tokens + patt.addParseAction(remove_LABEL) + print(patt.parseString("AAB 123 321").dump()) + prints:: + ['AAB', '123', '321'] + - LABEL: AAB + + ['AAB', '123', '321'] + """ + if not args: + args = [-1] + for k,v in kwargs.items(): + if k == 'default': + args = (args[0], v) + else: + raise TypeError("pop() got an unexpected keyword argument '%s'" % k) + if (isinstance(args[0], int) or + len(args) == 1 or + args[0] in self): + index = args[0] + ret = self[index] + del self[index] + return ret + else: + defaultvalue = args[1] + return defaultvalue + + def get(self, key, defaultValue=None): + """ + Returns named result matching the given key, or if there is no + such name, then returns the given C{defaultValue} or C{None} if no + C{defaultValue} is specified. + + Similar to C{dict.get()}. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString("1999/12/31") + print(result.get("year")) # -> '1999' + print(result.get("hour", "not specified")) # -> 'not specified' + print(result.get("hour")) # -> None + """ + if key in self: + return self[key] + else: + return defaultValue + + def insert( self, index, insStr ): + """ + Inserts new element at location index in the list of parsed tokens. + + Similar to C{list.insert()}. + + Example:: + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + + # use a parse action to insert the parse location in the front of the parsed results + def insert_locn(locn, tokens): + tokens.insert(0, locn) + print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321'] + """ + self.__toklist.insert(index, insStr) + # fixup indices in token dictionary + for name,occurrences in self.__tokdict.items(): + for k, (value, position) in enumerate(occurrences): + occurrences[k] = _ParseResultsWithOffset(value, position + (position > index)) + + def append( self, item ): + """ + Add single element to end of ParseResults list of elements. + + Example:: + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + + # use a parse action to compute the sum of the parsed integers, and add it to the end + def append_sum(tokens): + tokens.append(sum(map(int, tokens))) + print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444] + """ + self.__toklist.append(item) + + def extend( self, itemseq ): + """ + Add sequence of elements to end of ParseResults list of elements. + + Example:: + patt = OneOrMore(Word(alphas)) + + # use a parse action to append the reverse of the matched strings, to make a palindrome + def make_palindrome(tokens): + tokens.extend(reversed([t[::-1] for t in tokens])) + return ''.join(tokens) + print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl' + """ + if isinstance(itemseq, ParseResults): + self += itemseq + else: + self.__toklist.extend(itemseq) + + def clear( self ): + """ + Clear all elements and results names. + """ + del self.__toklist[:] + self.__tokdict.clear() + + def __getattr__( self, name ): + try: + return self[name] + except KeyError: + return "" + + if name in self.__tokdict: + if name not in self.__accumNames: + return self.__tokdict[name][-1][0] + else: + return ParseResults([ v[0] for v in self.__tokdict[name] ]) + else: + return "" + + def __add__( self, other ): + ret = self.copy() + ret += other + return ret + + def __iadd__( self, other ): + if other.__tokdict: + offset = len(self.__toklist) + addoffset = lambda a: offset if a<0 else a+offset + otheritems = other.__tokdict.items() + otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) ) + for (k,vlist) in otheritems for v in vlist] + for k,v in otherdictitems: + self[k] = v + if isinstance(v[0],ParseResults): + v[0].__parent = wkref(self) + + self.__toklist += other.__toklist + self.__accumNames.update( other.__accumNames ) + return self + + def __radd__(self, other): + if isinstance(other,int) and other == 0: + # useful for merging many ParseResults using sum() builtin + return self.copy() + else: + # this may raise a TypeError - so be it + return other + self + + def __repr__( self ): + return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) ) + + def __str__( self ): + return '[' + ', '.join(_ustr(i) if isinstance(i, ParseResults) else repr(i) for i in self.__toklist) + ']' + + def _asStringList( self, sep='' ): + out = [] + for item in self.__toklist: + if out and sep: + out.append(sep) + if isinstance( item, ParseResults ): + out += item._asStringList() + else: + out.append( _ustr(item) ) + return out + + def asList( self ): + """ + Returns the parse results as a nested list of matching tokens, all converted to strings. + + Example:: + patt = OneOrMore(Word(alphas)) + result = patt.parseString("sldkj lsdkj sldkj") + # even though the result prints in string-like form, it is actually a pyparsing ParseResults + print(type(result), result) # -> <class 'pyparsing.ParseResults'> ['sldkj', 'lsdkj', 'sldkj'] + + # Use asList() to create an actual list + result_list = result.asList() + print(type(result_list), result_list) # -> <class 'list'> ['sldkj', 'lsdkj', 'sldkj'] + """ + return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist] + + def asDict( self ): + """ + Returns the named parse results as a nested dictionary. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString('12/31/1999') + print(type(result), repr(result)) # -> <class 'pyparsing.ParseResults'> (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]}) + + result_dict = result.asDict() + print(type(result_dict), repr(result_dict)) # -> <class 'dict'> {'day': '1999', 'year': '12', 'month': '31'} + + # even though a ParseResults supports dict-like access, sometime you just need to have a dict + import json + print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable + print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"} + """ + if PY_3: + item_fn = self.items + else: + item_fn = self.iteritems + + def toItem(obj): + if isinstance(obj, ParseResults): + if obj.haskeys(): + return obj.asDict() + else: + return [toItem(v) for v in obj] + else: + return obj + + return dict((k,toItem(v)) for k,v in item_fn()) + + def copy( self ): + """ + Returns a new copy of a C{ParseResults} object. + """ + ret = ParseResults( self.__toklist ) + ret.__tokdict = self.__tokdict.copy() + ret.__parent = self.__parent + ret.__accumNames.update( self.__accumNames ) + ret.__name = self.__name + return ret + + def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ): + """ + (Deprecated) Returns the parse results as XML. Tags are created for tokens and lists that have defined results names. + """ + nl = "\n" + out = [] + namedItems = dict((v[1],k) for (k,vlist) in self.__tokdict.items() + for v in vlist) + nextLevelIndent = indent + " " + + # collapse out indents if formatting is not desired + if not formatted: + indent = "" + nextLevelIndent = "" + nl = "" + + selfTag = None + if doctag is not None: + selfTag = doctag + else: + if self.__name: + selfTag = self.__name + + if not selfTag: + if namedItemsOnly: + return "" + else: + selfTag = "ITEM" + + out += [ nl, indent, "<", selfTag, ">" ] + + for i,res in enumerate(self.__toklist): + if isinstance(res,ParseResults): + if i in namedItems: + out += [ res.asXML(namedItems[i], + namedItemsOnly and doctag is None, + nextLevelIndent, + formatted)] + else: + out += [ res.asXML(None, + namedItemsOnly and doctag is None, + nextLevelIndent, + formatted)] + else: + # individual token, see if there is a name for it + resTag = None + if i in namedItems: + resTag = namedItems[i] + if not resTag: + if namedItemsOnly: + continue + else: + resTag = "ITEM" + xmlBodyText = _xml_escape(_ustr(res)) + out += [ nl, nextLevelIndent, "<", resTag, ">", + xmlBodyText, + "</", resTag, ">" ] + + out += [ nl, indent, "</", selfTag, ">" ] + return "".join(out) + + def __lookup(self,sub): + for k,vlist in self.__tokdict.items(): + for v,loc in vlist: + if sub is v: + return k + return None + + def getName(self): + r""" + Returns the results name for this token expression. Useful when several + different expressions might match at a particular location. + + Example:: + integer = Word(nums) + ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d") + house_number_expr = Suppress('#') + Word(nums, alphanums) + user_data = (Group(house_number_expr)("house_number") + | Group(ssn_expr)("ssn") + | Group(integer)("age")) + user_info = OneOrMore(user_data) + + result = user_info.parseString("22 111-22-3333 #221B") + for item in result: + print(item.getName(), ':', item[0]) + prints:: + age : 22 + ssn : 111-22-3333 + house_number : 221B + """ + if self.__name: + return self.__name + elif self.__parent: + par = self.__parent() + if par: + return par.__lookup(self) + else: + return None + elif (len(self) == 1 and + len(self.__tokdict) == 1 and + next(iter(self.__tokdict.values()))[0][1] in (0,-1)): + return next(iter(self.__tokdict.keys())) + else: + return None + + def dump(self, indent='', depth=0, full=True): + """ + Diagnostic method for listing out the contents of a C{ParseResults}. + Accepts an optional C{indent} argument so that this string can be embedded + in a nested display of other data. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString('12/31/1999') + print(result.dump()) + prints:: + ['12', '/', '31', '/', '1999'] + - day: 1999 + - month: 31 + - year: 12 + """ + out = [] + NL = '\n' + out.append( indent+_ustr(self.asList()) ) + if full: + if self.haskeys(): + items = sorted((str(k), v) for k,v in self.items()) + for k,v in items: + if out: + out.append(NL) + out.append( "%s%s- %s: " % (indent,(' '*depth), k) ) + if isinstance(v,ParseResults): + if v: + out.append( v.dump(indent,depth+1) ) + else: + out.append(_ustr(v)) + else: + out.append(repr(v)) + elif any(isinstance(vv,ParseResults) for vv in self): + v = self + for i,vv in enumerate(v): + if isinstance(vv,ParseResults): + out.append("\n%s%s[%d]:\n%s%s%s" % (indent,(' '*(depth)),i,indent,(' '*(depth+1)),vv.dump(indent,depth+1) )) + else: + out.append("\n%s%s[%d]:\n%s%s%s" % (indent,(' '*(depth)),i,indent,(' '*(depth+1)),_ustr(vv))) + + return "".join(out) + + def pprint(self, *args, **kwargs): + """ + Pretty-printer for parsed results as a list, using the C{pprint} module. + Accepts additional positional or keyword args as defined for the + C{pprint.pprint} method. (U{http://docs.python.org/3/library/pprint.html#pprint.pprint}) + + Example:: + ident = Word(alphas, alphanums) + num = Word(nums) + func = Forward() + term = ident | num | Group('(' + func + ')') + func <<= ident + Group(Optional(delimitedList(term))) + result = func.parseString("fna a,b,(fnb c,d,200),100") + result.pprint(width=40) + prints:: + ['fna', + ['a', + 'b', + ['(', 'fnb', ['c', 'd', '200'], ')'], + '100']] + """ + pprint.pprint(self.asList(), *args, **kwargs) + + # add support for pickle protocol + def __getstate__(self): + return ( self.__toklist, + ( self.__tokdict.copy(), + self.__parent is not None and self.__parent() or None, + self.__accumNames, + self.__name ) ) + + def __setstate__(self,state): + self.__toklist = state[0] + (self.__tokdict, + par, + inAccumNames, + self.__name) = state[1] + self.__accumNames = {} + self.__accumNames.update(inAccumNames) + if par is not None: + self.__parent = wkref(par) + else: + self.__parent = None + + def __getnewargs__(self): + return self.__toklist, self.__name, self.__asList, self.__modal + + def __dir__(self): + return (dir(type(self)) + list(self.keys())) + +MutableMapping.register(ParseResults) + +def col (loc,strg): + """Returns current column within a string, counting newlines as line separators. + The first column is number 1. + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See L{I{ParserElement.parseString}<ParserElement.parseString>} for more information + on parsing strings containing C{<TAB>}s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + """ + s = strg + return 1 if 0<loc<len(s) and s[loc-1] == '\n' else loc - s.rfind("\n", 0, loc) + +def lineno(loc,strg): + """Returns current line number within a string, counting newlines as line separators. + The first line is number 1. + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See L{I{ParserElement.parseString}<ParserElement.parseString>} for more information + on parsing strings containing C{<TAB>}s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + """ + return strg.count("\n",0,loc) + 1 + +def line( loc, strg ): + """Returns the line of text containing loc within a string, counting newlines as line separators. + """ + lastCR = strg.rfind("\n", 0, loc) + nextCR = strg.find("\n", loc) + if nextCR >= 0: + return strg[lastCR+1:nextCR] + else: + return strg[lastCR+1:] + +def _defaultStartDebugAction( instring, loc, expr ): + print (("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) ))) + +def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ): + print ("Matched " + _ustr(expr) + " -> " + str(toks.asList())) + +def _defaultExceptionDebugAction( instring, loc, expr, exc ): + print ("Exception raised:" + _ustr(exc)) + +def nullDebugAction(*args): + """'Do-nothing' debug action, to suppress debugging output during parsing.""" + pass + +# Only works on Python 3.x - nonlocal is toxic to Python 2 installs +#~ 'decorator to trim function calls to match the arity of the target' +#~ def _trim_arity(func, maxargs=3): + #~ if func in singleArgBuiltins: + #~ return lambda s,l,t: func(t) + #~ limit = 0 + #~ foundArity = False + #~ def wrapper(*args): + #~ nonlocal limit,foundArity + #~ while 1: + #~ try: + #~ ret = func(*args[limit:]) + #~ foundArity = True + #~ return ret + #~ except TypeError: + #~ if limit == maxargs or foundArity: + #~ raise + #~ limit += 1 + #~ continue + #~ return wrapper + +# this version is Python 2.x-3.x cross-compatible +'decorator to trim function calls to match the arity of the target' +def _trim_arity(func, maxargs=2): + if func in singleArgBuiltins: + return lambda s,l,t: func(t) + limit = [0] + foundArity = [False] + + # traceback return data structure changed in Py3.5 - normalize back to plain tuples + if system_version[:2] >= (3,5): + def extract_stack(limit=0): + # special handling for Python 3.5.0 - extra deep call stack by 1 + offset = -3 if system_version == (3,5,0) else -2 + frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset] + return [frame_summary[:2]] + def extract_tb(tb, limit=0): + frames = traceback.extract_tb(tb, limit=limit) + frame_summary = frames[-1] + return [frame_summary[:2]] + else: + extract_stack = traceback.extract_stack + extract_tb = traceback.extract_tb + + # synthesize what would be returned by traceback.extract_stack at the call to + # user's parse action 'func', so that we don't incur call penalty at parse time + + LINE_DIFF = 6 + # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND + # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!! + this_line = extract_stack(limit=2)[-1] + pa_call_line_synth = (this_line[0], this_line[1]+LINE_DIFF) + + def wrapper(*args): + while 1: + try: + ret = func(*args[limit[0]:]) + foundArity[0] = True + return ret + except TypeError: + # re-raise TypeErrors if they did not come from our arity testing + if foundArity[0]: + raise + else: + try: + tb = sys.exc_info()[-1] + if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth: + raise + finally: + del tb + + if limit[0] <= maxargs: + limit[0] += 1 + continue + raise + + # copy func name to wrapper for sensible debug output + func_name = "<parse action>" + try: + func_name = getattr(func, '__name__', + getattr(func, '__class__').__name__) + except Exception: + func_name = str(func) + wrapper.__name__ = func_name + + return wrapper + +class ParserElement(object): + """Abstract base level parser element class.""" + DEFAULT_WHITE_CHARS = " \n\t\r" + verbose_stacktrace = False + + @staticmethod + def setDefaultWhitespaceChars( chars ): + r""" + Overrides the default whitespace chars + + Example:: + # default whitespace chars are space, <TAB> and newline + OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def', 'ghi', 'jkl'] + + # change to just treat newline as significant + ParserElement.setDefaultWhitespaceChars(" \t") + OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def'] + """ + ParserElement.DEFAULT_WHITE_CHARS = chars + + @staticmethod + def inlineLiteralsUsing(cls): + """ + Set class to be used for inclusion of string literals into a parser. + + Example:: + # default literal class used is Literal + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] + + + # change to Suppress + ParserElement.inlineLiteralsUsing(Suppress) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + date_str.parseString("1999/12/31") # -> ['1999', '12', '31'] + """ + ParserElement._literalStringClass = cls + + def __init__( self, savelist=False ): + self.parseAction = list() + self.failAction = None + #~ self.name = "<unknown>" # don't define self.name, let subclasses try/except upcall + self.strRepr = None + self.resultsName = None + self.saveAsList = savelist + self.skipWhitespace = True + self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS + self.copyDefaultWhiteChars = True + self.mayReturnEmpty = False # used when checking for left-recursion + self.keepTabs = False + self.ignoreExprs = list() + self.debug = False + self.streamlined = False + self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index + self.errmsg = "" + self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all) + self.debugActions = ( None, None, None ) #custom debug actions + self.re = None + self.callPreparse = True # used to avoid redundant calls to preParse + self.callDuringTry = False + + def copy( self ): + """ + Make a copy of this C{ParserElement}. Useful for defining different parse actions + for the same parsing pattern, using copies of the original parse element. + + Example:: + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + integerK = integer.copy().addParseAction(lambda toks: toks[0]*1024) + Suppress("K") + integerM = integer.copy().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M") + + print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M")) + prints:: + [5120, 100, 655360, 268435456] + Equivalent form of C{expr.copy()} is just C{expr()}:: + integerM = integer().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M") + """ + cpy = copy.copy( self ) + cpy.parseAction = self.parseAction[:] + cpy.ignoreExprs = self.ignoreExprs[:] + if self.copyDefaultWhiteChars: + cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS + return cpy + + def setName( self, name ): + """ + Define name for this expression, makes debugging and exception messages clearer. + + Example:: + Word(nums).parseString("ABC") # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1) + Word(nums).setName("integer").parseString("ABC") # -> Exception: Expected integer (at char 0), (line:1, col:1) + """ + self.name = name + self.errmsg = "Expected " + self.name + if hasattr(self,"exception"): + self.exception.msg = self.errmsg + return self + + def setResultsName( self, name, listAllMatches=False ): + """ + Define name for referencing matching tokens as a nested attribute + of the returned parse results. + NOTE: this returns a *copy* of the original C{ParserElement} object; + this is so that the client can define a basic element, such as an + integer, and reference it in multiple places with different names. + + You can also set results names using the abbreviated syntax, + C{expr("name")} in place of C{expr.setResultsName("name")} - + see L{I{__call__}<__call__>}. + + Example:: + date_str = (integer.setResultsName("year") + '/' + + integer.setResultsName("month") + '/' + + integer.setResultsName("day")) + + # equivalent form: + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + """ + newself = self.copy() + if name.endswith("*"): + name = name[:-1] + listAllMatches=True + newself.resultsName = name + newself.modalResults = not listAllMatches + return newself + + def setBreak(self,breakFlag = True): + """Method to invoke the Python pdb debugger when this element is + about to be parsed. Set C{breakFlag} to True to enable, False to + disable. + """ + if breakFlag: + _parseMethod = self._parse + def breaker(instring, loc, doActions=True, callPreParse=True): + import pdb + pdb.set_trace() + return _parseMethod( instring, loc, doActions, callPreParse ) + breaker._originalParseMethod = _parseMethod + self._parse = breaker + else: + if hasattr(self._parse,"_originalParseMethod"): + self._parse = self._parse._originalParseMethod + return self + + def setParseAction( self, *fns, **kwargs ): + """ + Define one or more actions to perform when successfully matching parse element definition. + Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)}, + C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where: + - s = the original string being parsed (see note below) + - loc = the location of the matching substring + - toks = a list of the matched tokens, packaged as a C{L{ParseResults}} object + If the functions in fns modify the tokens, they can return them as the return + value from fn, and the modified list of tokens will replace the original. + Otherwise, fn does not need to return any value. + + Optional keyword arguments: + - callDuringTry = (default=C{False}) indicate if parse action should be run during lookaheads and alternate testing + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See L{I{parseString}<parseString>} for more information + on parsing strings containing C{<TAB>}s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + + Example:: + integer = Word(nums) + date_str = integer + '/' + integer + '/' + integer + + date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] + + # use parse action to convert to ints at parse time + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + date_str = integer + '/' + integer + '/' + integer + + # note that integer fields are now ints, not strings + date_str.parseString("1999/12/31") # -> [1999, '/', 12, '/', 31] + """ + self.parseAction = list(map(_trim_arity, list(fns))) + self.callDuringTry = kwargs.get("callDuringTry", False) + return self + + def addParseAction( self, *fns, **kwargs ): + """ + Add one or more parse actions to expression's list of parse actions. See L{I{setParseAction}<setParseAction>}. + + See examples in L{I{copy}<copy>}. + """ + self.parseAction += list(map(_trim_arity, list(fns))) + self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False) + return self + + def addCondition(self, *fns, **kwargs): + """Add a boolean predicate function to expression's list of parse actions. See + L{I{setParseAction}<setParseAction>} for function call signatures. Unlike C{setParseAction}, + functions passed to C{addCondition} need to return boolean success/fail of the condition. + + Optional keyword arguments: + - message = define a custom message to be used in the raised exception + - fatal = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException + + Example:: + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + year_int = integer.copy() + year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later") + date_str = year_int + '/' + integer + '/' + integer + + result = date_str.parseString("1999/12/31") # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1) + """ + msg = kwargs.get("message", "failed user-defined condition") + exc_type = ParseFatalException if kwargs.get("fatal", False) else ParseException + for fn in fns: + def pa(s,l,t): + if not bool(_trim_arity(fn)(s,l,t)): + raise exc_type(s,l,msg) + self.parseAction.append(pa) + self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False) + return self + + def setFailAction( self, fn ): + """Define action to perform if parsing fails at this expression. + Fail acton fn is a callable function that takes the arguments + C{fn(s,loc,expr,err)} where: + - s = string being parsed + - loc = location where expression match was attempted and failed + - expr = the parse expression that failed + - err = the exception thrown + The function returns no value. It may throw C{L{ParseFatalException}} + if it is desired to stop parsing immediately.""" + self.failAction = fn + return self + + def _skipIgnorables( self, instring, loc ): + exprsFound = True + while exprsFound: + exprsFound = False + for e in self.ignoreExprs: + try: + while 1: + loc,dummy = e._parse( instring, loc ) + exprsFound = True + except ParseException: + pass + return loc + + def preParse( self, instring, loc ): + if self.ignoreExprs: + loc = self._skipIgnorables( instring, loc ) + + if self.skipWhitespace: + wt = self.whiteChars + instrlen = len(instring) + while loc < instrlen and instring[loc] in wt: + loc += 1 + + return loc + + def parseImpl( self, instring, loc, doActions=True ): + return loc, [] + + def postParse( self, instring, loc, tokenlist ): + return tokenlist + + #~ @profile + def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ): + debugging = ( self.debug ) #and doActions ) + + if debugging or self.failAction: + #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )) + if (self.debugActions[0] ): + self.debugActions[0]( instring, loc, self ) + if callPreParse and self.callPreparse: + preloc = self.preParse( instring, loc ) + else: + preloc = loc + tokensStart = preloc + try: + try: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + except IndexError: + raise ParseException( instring, len(instring), self.errmsg, self ) + except ParseBaseException as err: + #~ print ("Exception raised:", err) + if self.debugActions[2]: + self.debugActions[2]( instring, tokensStart, self, err ) + if self.failAction: + self.failAction( instring, tokensStart, self, err ) + raise + else: + if callPreParse and self.callPreparse: + preloc = self.preParse( instring, loc ) + else: + preloc = loc + tokensStart = preloc + if self.mayIndexError or preloc >= len(instring): + try: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + except IndexError: + raise ParseException( instring, len(instring), self.errmsg, self ) + else: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + + tokens = self.postParse( instring, loc, tokens ) + + retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults ) + if self.parseAction and (doActions or self.callDuringTry): + if debugging: + try: + for fn in self.parseAction: + tokens = fn( instring, tokensStart, retTokens ) + if tokens is not None: + retTokens = ParseResults( tokens, + self.resultsName, + asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), + modal=self.modalResults ) + except ParseBaseException as err: + #~ print "Exception raised in user parse action:", err + if (self.debugActions[2] ): + self.debugActions[2]( instring, tokensStart, self, err ) + raise + else: + for fn in self.parseAction: + tokens = fn( instring, tokensStart, retTokens ) + if tokens is not None: + retTokens = ParseResults( tokens, + self.resultsName, + asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), + modal=self.modalResults ) + if debugging: + #~ print ("Matched",self,"->",retTokens.asList()) + if (self.debugActions[1] ): + self.debugActions[1]( instring, tokensStart, loc, self, retTokens ) + + return loc, retTokens + + def tryParse( self, instring, loc ): + try: + return self._parse( instring, loc, doActions=False )[0] + except ParseFatalException: + raise ParseException( instring, loc, self.errmsg, self) + + def canParseNext(self, instring, loc): + try: + self.tryParse(instring, loc) + except (ParseException, IndexError): + return False + else: + return True + + class _UnboundedCache(object): + def __init__(self): + cache = {} + self.not_in_cache = not_in_cache = object() + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + + def clear(self): + cache.clear() + + def cache_len(self): + return len(cache) + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + self.__len__ = types.MethodType(cache_len, self) + + if _OrderedDict is not None: + class _FifoCache(object): + def __init__(self, size): + self.not_in_cache = not_in_cache = object() + + cache = _OrderedDict() + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + while len(cache) > size: + try: + cache.popitem(False) + except KeyError: + pass + + def clear(self): + cache.clear() + + def cache_len(self): + return len(cache) + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + self.__len__ = types.MethodType(cache_len, self) + + else: + class _FifoCache(object): + def __init__(self, size): + self.not_in_cache = not_in_cache = object() + + cache = {} + key_fifo = collections.deque([], size) + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + while len(key_fifo) > size: + cache.pop(key_fifo.popleft(), None) + key_fifo.append(key) + + def clear(self): + cache.clear() + key_fifo.clear() + + def cache_len(self): + return len(cache) + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + self.__len__ = types.MethodType(cache_len, self) + + # argument cache for optimizing repeated calls when backtracking through recursive expressions + packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail + packrat_cache_lock = RLock() + packrat_cache_stats = [0, 0] + + # this method gets repeatedly called during backtracking with the same arguments - + # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression + def _parseCache( self, instring, loc, doActions=True, callPreParse=True ): + HIT, MISS = 0, 1 + lookup = (self, instring, loc, callPreParse, doActions) + with ParserElement.packrat_cache_lock: + cache = ParserElement.packrat_cache + value = cache.get(lookup) + if value is cache.not_in_cache: + ParserElement.packrat_cache_stats[MISS] += 1 + try: + value = self._parseNoCache(instring, loc, doActions, callPreParse) + except ParseBaseException as pe: + # cache a copy of the exception, without the traceback + cache.set(lookup, pe.__class__(*pe.args)) + raise + else: + cache.set(lookup, (value[0], value[1].copy())) + return value + else: + ParserElement.packrat_cache_stats[HIT] += 1 + if isinstance(value, Exception): + raise value + return (value[0], value[1].copy()) + + _parse = _parseNoCache + + @staticmethod + def resetCache(): + ParserElement.packrat_cache.clear() + ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats) + + _packratEnabled = False + @staticmethod + def enablePackrat(cache_size_limit=128): + """Enables "packrat" parsing, which adds memoizing to the parsing logic. + Repeated parse attempts at the same string location (which happens + often in many complex grammars) can immediately return a cached value, + instead of re-executing parsing/validating code. Memoizing is done of + both valid results and parsing exceptions. + + Parameters: + - cache_size_limit - (default=C{128}) - if an integer value is provided + will limit the size of the packrat cache; if None is passed, then + the cache size will be unbounded; if 0 is passed, the cache will + be effectively disabled. + + This speedup may break existing programs that use parse actions that + have side-effects. For this reason, packrat parsing is disabled when + you first import pyparsing. To activate the packrat feature, your + program must call the class method C{ParserElement.enablePackrat()}. If + your program uses C{psyco} to "compile as you go", you must call + C{enablePackrat} before calling C{psyco.full()}. If you do not do this, + Python will crash. For best results, call C{enablePackrat()} immediately + after importing pyparsing. + + Example:: + import pyparsing + pyparsing.ParserElement.enablePackrat() + """ + if not ParserElement._packratEnabled: + ParserElement._packratEnabled = True + if cache_size_limit is None: + ParserElement.packrat_cache = ParserElement._UnboundedCache() + else: + ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit) + ParserElement._parse = ParserElement._parseCache + + def parseString( self, instring, parseAll=False ): + """ + Execute the parse expression with the given string. + This is the main interface to the client code, once the complete + expression has been built. + + If you want the grammar to require that the entire input string be + successfully parsed, then set C{parseAll} to True (equivalent to ending + the grammar with C{L{StringEnd()}}). + + Note: C{parseString} implicitly calls C{expandtabs()} on the input string, + in order to report proper column numbers in parse actions. + If the input string contains tabs and + the grammar uses parse actions that use the C{loc} argument to index into the + string being parsed, you can ensure you have a consistent view of the input + string by: + - calling C{parseWithTabs} on your grammar before calling C{parseString} + (see L{I{parseWithTabs}<parseWithTabs>}) + - define your parse action using the full C{(s,loc,toks)} signature, and + reference the input string using the parse action's C{s} argument + - explictly expand the tabs in your input string before calling + C{parseString} + + Example:: + Word('a').parseString('aaaaabaaa') # -> ['aaaaa'] + Word('a').parseString('aaaaabaaa', parseAll=True) # -> Exception: Expected end of text + """ + ParserElement.resetCache() + if not self.streamlined: + self.streamline() + #~ self.saveAsList = True + for e in self.ignoreExprs: + e.streamline() + if not self.keepTabs: + instring = instring.expandtabs() + try: + loc, tokens = self._parse( instring, 0 ) + if parseAll: + loc = self.preParse( instring, loc ) + se = Empty() + StringEnd() + se._parse( instring, loc ) + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + else: + return tokens + + def scanString( self, instring, maxMatches=_MAX_INT, overlap=False ): + """ + Scan the input string for expression matches. Each match will return the + matching tokens, start location, and end location. May be called with optional + C{maxMatches} argument, to clip scanning after 'n' matches are found. If + C{overlap} is specified, then overlapping matches will be reported. + + Note that the start and end locations are reported relative to the string + being parsed. See L{I{parseString}<parseString>} for more information on parsing + strings with embedded tabs. + + Example:: + source = "sldjf123lsdjjkf345sldkjf879lkjsfd987" + print(source) + for tokens,start,end in Word(alphas).scanString(source): + print(' '*start + '^'*(end-start)) + print(' '*start + tokens[0]) + + prints:: + + sldjf123lsdjjkf345sldkjf879lkjsfd987 + ^^^^^ + sldjf + ^^^^^^^ + lsdjjkf + ^^^^^^ + sldkjf + ^^^^^^ + lkjsfd + """ + if not self.streamlined: + self.streamline() + for e in self.ignoreExprs: + e.streamline() + + if not self.keepTabs: + instring = _ustr(instring).expandtabs() + instrlen = len(instring) + loc = 0 + preparseFn = self.preParse + parseFn = self._parse + ParserElement.resetCache() + matches = 0 + try: + while loc <= instrlen and matches < maxMatches: + try: + preloc = preparseFn( instring, loc ) + nextLoc,tokens = parseFn( instring, preloc, callPreParse=False ) + except ParseException: + loc = preloc+1 + else: + if nextLoc > loc: + matches += 1 + yield tokens, preloc, nextLoc + if overlap: + nextloc = preparseFn( instring, loc ) + if nextloc > loc: + loc = nextLoc + else: + loc += 1 + else: + loc = nextLoc + else: + loc = preloc+1 + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + + def transformString( self, instring ): + """ + Extension to C{L{scanString}}, to modify matching text with modified tokens that may + be returned from a parse action. To use C{transformString}, define a grammar and + attach a parse action to it that modifies the returned token list. + Invoking C{transformString()} on a target string will then scan for matches, + and replace the matched text patterns according to the logic in the parse + action. C{transformString()} returns the resulting transformed string. + + Example:: + wd = Word(alphas) + wd.setParseAction(lambda toks: toks[0].title()) + + print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york.")) + Prints:: + Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York. + """ + out = [] + lastE = 0 + # force preservation of <TAB>s, to minimize unwanted transformation of string, and to + # keep string locs straight between transformString and scanString + self.keepTabs = True + try: + for t,s,e in self.scanString( instring ): + out.append( instring[lastE:s] ) + if t: + if isinstance(t,ParseResults): + out += t.asList() + elif isinstance(t,list): + out += t + else: + out.append(t) + lastE = e + out.append(instring[lastE:]) + out = [o for o in out if o] + return "".join(map(_ustr,_flatten(out))) + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + + def searchString( self, instring, maxMatches=_MAX_INT ): + """ + Another extension to C{L{scanString}}, simplifying the access to the tokens found + to match the given parse expression. May be called with optional + C{maxMatches} argument, to clip searching after 'n' matches are found. + + Example:: + # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters + cap_word = Word(alphas.upper(), alphas.lower()) + + print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")) + + # the sum() builtin can be used to merge results into a single ParseResults object + print(sum(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity"))) + prints:: + [['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']] + ['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity'] + """ + try: + return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ]) + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + + def split(self, instring, maxsplit=_MAX_INT, includeSeparators=False): + """ + Generator method to split a string using the given expression as a separator. + May be called with optional C{maxsplit} argument, to limit the number of splits; + and the optional C{includeSeparators} argument (default=C{False}), if the separating + matching text should be included in the split results. + + Example:: + punc = oneOf(list(".,;:/-!?")) + print(list(punc.split("This, this?, this sentence, is badly punctuated!"))) + prints:: + ['This', ' this', '', ' this sentence', ' is badly punctuated', ''] + """ + splits = 0 + last = 0 + for t,s,e in self.scanString(instring, maxMatches=maxsplit): + yield instring[last:s] + if includeSeparators: + yield t[0] + last = e + yield instring[last:] + + def __add__(self, other ): + """ + Implementation of + operator - returns C{L{And}}. Adding strings to a ParserElement + converts them to L{Literal}s by default. + + Example:: + greet = Word(alphas) + "," + Word(alphas) + "!" + hello = "Hello, World!" + print (hello, "->", greet.parseString(hello)) + Prints:: + Hello, World! -> ['Hello', ',', 'World', '!'] + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return And( [ self, other ] ) + + def __radd__(self, other ): + """ + Implementation of + operator when left operand is not a C{L{ParserElement}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other + self + + def __sub__(self, other): + """ + Implementation of - operator, returns C{L{And}} with error stop + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return self + And._ErrorStop() + other + + def __rsub__(self, other ): + """ + Implementation of - operator when left operand is not a C{L{ParserElement}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other - self + + def __mul__(self,other): + """ + Implementation of * operator, allows use of C{expr * 3} in place of + C{expr + expr + expr}. Expressions may also me multiplied by a 2-integer + tuple, similar to C{{min,max}} multipliers in regular expressions. Tuples + may also include C{None} as in: + - C{expr*(n,None)} or C{expr*(n,)} is equivalent + to C{expr*n + L{ZeroOrMore}(expr)} + (read as "at least n instances of C{expr}") + - C{expr*(None,n)} is equivalent to C{expr*(0,n)} + (read as "0 to n instances of C{expr}") + - C{expr*(None,None)} is equivalent to C{L{ZeroOrMore}(expr)} + - C{expr*(1,None)} is equivalent to C{L{OneOrMore}(expr)} + + Note that C{expr*(None,n)} does not raise an exception if + more than n exprs exist in the input stream; that is, + C{expr*(None,n)} does not enforce a maximum number of expr + occurrences. If this behavior is desired, then write + C{expr*(None,n) + ~expr} + """ + if isinstance(other,int): + minElements, optElements = other,0 + elif isinstance(other,tuple): + other = (other + (None, None))[:2] + if other[0] is None: + other = (0, other[1]) + if isinstance(other[0],int) and other[1] is None: + if other[0] == 0: + return ZeroOrMore(self) + if other[0] == 1: + return OneOrMore(self) + else: + return self*other[0] + ZeroOrMore(self) + elif isinstance(other[0],int) and isinstance(other[1],int): + minElements, optElements = other + optElements -= minElements + else: + raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1])) + else: + raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other)) + + if minElements < 0: + raise ValueError("cannot multiply ParserElement by negative value") + if optElements < 0: + raise ValueError("second tuple value must be greater or equal to first tuple value") + if minElements == optElements == 0: + raise ValueError("cannot multiply ParserElement by 0 or (0,0)") + + if (optElements): + def makeOptionalList(n): + if n>1: + return Optional(self + makeOptionalList(n-1)) + else: + return Optional(self) + if minElements: + if minElements == 1: + ret = self + makeOptionalList(optElements) + else: + ret = And([self]*minElements) + makeOptionalList(optElements) + else: + ret = makeOptionalList(optElements) + else: + if minElements == 1: + ret = self + else: + ret = And([self]*minElements) + return ret + + def __rmul__(self, other): + return self.__mul__(other) + + def __or__(self, other ): + """ + Implementation of | operator - returns C{L{MatchFirst}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return MatchFirst( [ self, other ] ) + + def __ror__(self, other ): + """ + Implementation of | operator when left operand is not a C{L{ParserElement}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other | self + + def __xor__(self, other ): + """ + Implementation of ^ operator - returns C{L{Or}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return Or( [ self, other ] ) + + def __rxor__(self, other ): + """ + Implementation of ^ operator when left operand is not a C{L{ParserElement}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other ^ self + + def __and__(self, other ): + """ + Implementation of & operator - returns C{L{Each}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return Each( [ self, other ] ) + + def __rand__(self, other ): + """ + Implementation of & operator when left operand is not a C{L{ParserElement}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other & self + + def __invert__( self ): + """ + Implementation of ~ operator - returns C{L{NotAny}} + """ + return NotAny( self ) + + def __call__(self, name=None): + """ + Shortcut for C{L{setResultsName}}, with C{listAllMatches=False}. + + If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be + passed as C{True}. + + If C{name} is omitted, same as calling C{L{copy}}. + + Example:: + # these are equivalent + userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno") + userdata = Word(alphas)("name") + Word(nums+"-")("socsecno") + """ + if name is not None: + return self.setResultsName(name) + else: + return self.copy() + + def suppress( self ): + """ + Suppresses the output of this C{ParserElement}; useful to keep punctuation from + cluttering up returned output. + """ + return Suppress( self ) + + def leaveWhitespace( self ): + """ + Disables the skipping of whitespace before matching the characters in the + C{ParserElement}'s defined pattern. This is normally only used internally by + the pyparsing module, but may be needed in some whitespace-sensitive grammars. + """ + self.skipWhitespace = False + return self + + def setWhitespaceChars( self, chars ): + """ + Overrides the default whitespace chars + """ + self.skipWhitespace = True + self.whiteChars = chars + self.copyDefaultWhiteChars = False + return self + + def parseWithTabs( self ): + """ + Overrides default behavior to expand C{<TAB>}s to spaces before parsing the input string. + Must be called before C{parseString} when the input grammar contains elements that + match C{<TAB>} characters. + """ + self.keepTabs = True + return self + + def ignore( self, other ): + """ + Define expression to be ignored (e.g., comments) while doing pattern + matching; may be called repeatedly, to define multiple comment or other + ignorable patterns. + + Example:: + patt = OneOrMore(Word(alphas)) + patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj'] + + patt.ignore(cStyleComment) + patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd'] + """ + if isinstance(other, basestring): + other = Suppress(other) + + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + self.ignoreExprs.append(other) + else: + self.ignoreExprs.append( Suppress( other.copy() ) ) + return self + + def setDebugActions( self, startAction, successAction, exceptionAction ): + """ + Enable display of debugging messages while doing pattern matching. + """ + self.debugActions = (startAction or _defaultStartDebugAction, + successAction or _defaultSuccessDebugAction, + exceptionAction or _defaultExceptionDebugAction) + self.debug = True + return self + + def setDebug( self, flag=True ): + """ + Enable display of debugging messages while doing pattern matching. + Set C{flag} to True to enable, False to disable. + + Example:: + wd = Word(alphas).setName("alphaword") + integer = Word(nums).setName("numword") + term = wd | integer + + # turn on debugging for wd + wd.setDebug() + + OneOrMore(term).parseString("abc 123 xyz 890") + + prints:: + Match alphaword at loc 0(1,1) + Matched alphaword -> ['abc'] + Match alphaword at loc 3(1,4) + Exception raised:Expected alphaword (at char 4), (line:1, col:5) + Match alphaword at loc 7(1,8) + Matched alphaword -> ['xyz'] + Match alphaword at loc 11(1,12) + Exception raised:Expected alphaword (at char 12), (line:1, col:13) + Match alphaword at loc 15(1,16) + Exception raised:Expected alphaword (at char 15), (line:1, col:16) + + The output shown is that produced by the default debug actions - custom debug actions can be + specified using L{setDebugActions}. Prior to attempting + to match the C{wd} expression, the debugging message C{"Match <exprname> at loc <n>(<line>,<col>)"} + is shown. Then if the parse succeeds, a C{"Matched"} message is shown, or an C{"Exception raised"} + message is shown. Also note the use of L{setName} to assign a human-readable name to the expression, + which makes debugging and exception messages easier to understand - for instance, the default + name created for the C{Word} expression without calling C{setName} is C{"W:(ABCD...)"}. + """ + if flag: + self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction ) + else: + self.debug = False + return self + + def __str__( self ): + return self.name + + def __repr__( self ): + return _ustr(self) + + def streamline( self ): + self.streamlined = True + self.strRepr = None + return self + + def checkRecursion( self, parseElementList ): + pass + + def validate( self, validateTrace=[] ): + """ + Check defined expressions for valid structure, check for infinite recursive definitions. + """ + self.checkRecursion( [] ) + + def parseFile( self, file_or_filename, parseAll=False ): + """ + Execute the parse expression on the given file or filename. + If a filename is specified (instead of a file object), + the entire file is opened, read, and closed before parsing. + """ + try: + file_contents = file_or_filename.read() + except AttributeError: + with open(file_or_filename, "r") as f: + file_contents = f.read() + try: + return self.parseString(file_contents, parseAll) + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + + def __eq__(self,other): + if isinstance(other, ParserElement): + return self is other or vars(self) == vars(other) + elif isinstance(other, basestring): + return self.matches(other) + else: + return super(ParserElement,self)==other + + def __ne__(self,other): + return not (self == other) + + def __hash__(self): + return hash(id(self)) + + def __req__(self,other): + return self == other + + def __rne__(self,other): + return not (self == other) + + def matches(self, testString, parseAll=True): + """ + Method for quick testing of a parser against a test string. Good for simple + inline microtests of sub expressions while building up larger parser. + + Parameters: + - testString - to test against this expression for a match + - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests + + Example:: + expr = Word(nums) + assert expr.matches("100") + """ + try: + self.parseString(_ustr(testString), parseAll=parseAll) + return True + except ParseBaseException: + return False + + def runTests(self, tests, parseAll=True, comment='#', fullDump=True, printResults=True, failureTests=False): + """ + Execute the parse expression on a series of test strings, showing each + test, the parsed results or where the parse failed. Quick and easy way to + run a parse expression against a list of sample strings. + + Parameters: + - tests - a list of separate test strings, or a multiline string of test strings + - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests + - comment - (default=C{'#'}) - expression for indicating embedded comments in the test + string; pass None to disable comment filtering + - fullDump - (default=C{True}) - dump results as list followed by results names in nested outline; + if False, only dump nested list + - printResults - (default=C{True}) prints test output to stdout + - failureTests - (default=C{False}) indicates if these tests are expected to fail parsing + + Returns: a (success, results) tuple, where success indicates that all tests succeeded + (or failed if C{failureTests} is True), and the results contain a list of lines of each + test's output + + Example:: + number_expr = pyparsing_common.number.copy() + + result = number_expr.runTests(''' + # unsigned integer + 100 + # negative integer + -100 + # float with scientific notation + 6.02e23 + # integer with scientific notation + 1e-12 + ''') + print("Success" if result[0] else "Failed!") + + result = number_expr.runTests(''' + # stray character + 100Z + # missing leading digit before '.' + -.100 + # too many '.' + 3.14.159 + ''', failureTests=True) + print("Success" if result[0] else "Failed!") + prints:: + # unsigned integer + 100 + [100] + + # negative integer + -100 + [-100] + + # float with scientific notation + 6.02e23 + [6.02e+23] + + # integer with scientific notation + 1e-12 + [1e-12] + + Success + + # stray character + 100Z + ^ + FAIL: Expected end of text (at char 3), (line:1, col:4) + + # missing leading digit before '.' + -.100 + ^ + FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1) + + # too many '.' + 3.14.159 + ^ + FAIL: Expected end of text (at char 4), (line:1, col:5) + + Success + + Each test string must be on a single line. If you want to test a string that spans multiple + lines, create a test like this:: + + expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines") + + (Note that this is a raw string literal, you must include the leading 'r'.) + """ + if isinstance(tests, basestring): + tests = list(map(str.strip, tests.rstrip().splitlines())) + if isinstance(comment, basestring): + comment = Literal(comment) + allResults = [] + comments = [] + success = True + for t in tests: + if comment is not None and comment.matches(t, False) or comments and not t: + comments.append(t) + continue + if not t: + continue + out = ['\n'.join(comments), t] + comments = [] + try: + t = t.replace(r'\n','\n') + result = self.parseString(t, parseAll=parseAll) + out.append(result.dump(full=fullDump)) + success = success and not failureTests + except ParseBaseException as pe: + fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else "" + if '\n' in t: + out.append(line(pe.loc, t)) + out.append(' '*(col(pe.loc,t)-1) + '^' + fatal) + else: + out.append(' '*pe.loc + '^' + fatal) + out.append("FAIL: " + str(pe)) + success = success and failureTests + result = pe + except Exception as exc: + out.append("FAIL-EXCEPTION: " + str(exc)) + success = success and failureTests + result = exc + + if printResults: + if fullDump: + out.append('') + print('\n'.join(out)) + + allResults.append((t, result)) + + return success, allResults + + +class Token(ParserElement): + """ + Abstract C{ParserElement} subclass, for defining atomic matching patterns. + """ + def __init__( self ): + super(Token,self).__init__( savelist=False ) + + +class Empty(Token): + """ + An empty token, will always match. + """ + def __init__( self ): + super(Empty,self).__init__() + self.name = "Empty" + self.mayReturnEmpty = True + self.mayIndexError = False + + +class NoMatch(Token): + """ + A token that will never match. + """ + def __init__( self ): + super(NoMatch,self).__init__() + self.name = "NoMatch" + self.mayReturnEmpty = True + self.mayIndexError = False + self.errmsg = "Unmatchable token" + + def parseImpl( self, instring, loc, doActions=True ): + raise ParseException(instring, loc, self.errmsg, self) + + +class Literal(Token): + """ + Token to exactly match a specified string. + + Example:: + Literal('blah').parseString('blah') # -> ['blah'] + Literal('blah').parseString('blahfooblah') # -> ['blah'] + Literal('blah').parseString('bla') # -> Exception: Expected "blah" + + For case-insensitive matching, use L{CaselessLiteral}. + + For keyword matching (force word break before and after the matched string), + use L{Keyword} or L{CaselessKeyword}. + """ + def __init__( self, matchString ): + super(Literal,self).__init__() + self.match = matchString + self.matchLen = len(matchString) + try: + self.firstMatchChar = matchString[0] + except IndexError: + warnings.warn("null string passed to Literal; use Empty() instead", + SyntaxWarning, stacklevel=2) + self.__class__ = Empty + self.name = '"%s"' % _ustr(self.match) + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = False + self.mayIndexError = False + + # Performance tuning: this routine gets called a *lot* + # if this is a single character match string and the first character matches, + # short-circuit as quickly as possible, and avoid calling startswith + #~ @profile + def parseImpl( self, instring, loc, doActions=True ): + if (instring[loc] == self.firstMatchChar and + (self.matchLen==1 or instring.startswith(self.match,loc)) ): + return loc+self.matchLen, self.match + raise ParseException(instring, loc, self.errmsg, self) +_L = Literal +ParserElement._literalStringClass = Literal + +class Keyword(Token): + """ + Token to exactly match a specified string as a keyword, that is, it must be + immediately followed by a non-keyword character. Compare with C{L{Literal}}: + - C{Literal("if")} will match the leading C{'if'} in C{'ifAndOnlyIf'}. + - C{Keyword("if")} will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'} + Accepts two optional constructor arguments in addition to the keyword string: + - C{identChars} is a string of characters that would be valid identifier characters, + defaulting to all alphanumerics + "_" and "$" + - C{caseless} allows case-insensitive matching, default is C{False}. + + Example:: + Keyword("start").parseString("start") # -> ['start'] + Keyword("start").parseString("starting") # -> Exception + + For case-insensitive matching, use L{CaselessKeyword}. + """ + DEFAULT_KEYWORD_CHARS = alphanums+"_$" + + def __init__( self, matchString, identChars=None, caseless=False ): + super(Keyword,self).__init__() + if identChars is None: + identChars = Keyword.DEFAULT_KEYWORD_CHARS + self.match = matchString + self.matchLen = len(matchString) + try: + self.firstMatchChar = matchString[0] + except IndexError: + warnings.warn("null string passed to Keyword; use Empty() instead", + SyntaxWarning, stacklevel=2) + self.name = '"%s"' % self.match + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = False + self.mayIndexError = False + self.caseless = caseless + if caseless: + self.caselessmatch = matchString.upper() + identChars = identChars.upper() + self.identChars = set(identChars) + + def parseImpl( self, instring, loc, doActions=True ): + if self.caseless: + if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and + (loc == 0 or instring[loc-1].upper() not in self.identChars) ): + return loc+self.matchLen, self.match + else: + if (instring[loc] == self.firstMatchChar and + (self.matchLen==1 or instring.startswith(self.match,loc)) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and + (loc == 0 or instring[loc-1] not in self.identChars) ): + return loc+self.matchLen, self.match + raise ParseException(instring, loc, self.errmsg, self) + + def copy(self): + c = super(Keyword,self).copy() + c.identChars = Keyword.DEFAULT_KEYWORD_CHARS + return c + + @staticmethod + def setDefaultKeywordChars( chars ): + """Overrides the default Keyword chars + """ + Keyword.DEFAULT_KEYWORD_CHARS = chars + +class CaselessLiteral(Literal): + """ + Token to match a specified string, ignoring case of letters. + Note: the matched results will always be in the case of the given + match string, NOT the case of the input text. + + Example:: + OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD'] + + (Contrast with example for L{CaselessKeyword}.) + """ + def __init__( self, matchString ): + super(CaselessLiteral,self).__init__( matchString.upper() ) + # Preserve the defining literal. + self.returnString = matchString + self.name = "'%s'" % self.returnString + self.errmsg = "Expected " + self.name + + def parseImpl( self, instring, loc, doActions=True ): + if instring[ loc:loc+self.matchLen ].upper() == self.match: + return loc+self.matchLen, self.returnString + raise ParseException(instring, loc, self.errmsg, self) + +class CaselessKeyword(Keyword): + """ + Caseless version of L{Keyword}. + + Example:: + OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD'] + + (Contrast with example for L{CaselessLiteral}.) + """ + def __init__( self, matchString, identChars=None ): + super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True ) + + def parseImpl( self, instring, loc, doActions=True ): + if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ): + return loc+self.matchLen, self.match + raise ParseException(instring, loc, self.errmsg, self) + +class CloseMatch(Token): + """ + A variation on L{Literal} which matches "close" matches, that is, + strings with at most 'n' mismatching characters. C{CloseMatch} takes parameters: + - C{match_string} - string to be matched + - C{maxMismatches} - (C{default=1}) maximum number of mismatches allowed to count as a match + + The results from a successful parse will contain the matched text from the input string and the following named results: + - C{mismatches} - a list of the positions within the match_string where mismatches were found + - C{original} - the original match_string used to compare against the input string + + If C{mismatches} is an empty list, then the match was an exact match. + + Example:: + patt = CloseMatch("ATCATCGAATGGA") + patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']}) + patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1) + + # exact match + patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']}) + + # close match allowing up to 2 mismatches + patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2) + patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']}) + """ + def __init__(self, match_string, maxMismatches=1): + super(CloseMatch,self).__init__() + self.name = match_string + self.match_string = match_string + self.maxMismatches = maxMismatches + self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches) + self.mayIndexError = False + self.mayReturnEmpty = False + + def parseImpl( self, instring, loc, doActions=True ): + start = loc + instrlen = len(instring) + maxloc = start + len(self.match_string) + + if maxloc <= instrlen: + match_string = self.match_string + match_stringloc = 0 + mismatches = [] + maxMismatches = self.maxMismatches + + for match_stringloc,s_m in enumerate(zip(instring[loc:maxloc], self.match_string)): + src,mat = s_m + if src != mat: + mismatches.append(match_stringloc) + if len(mismatches) > maxMismatches: + break + else: + loc = match_stringloc + 1 + results = ParseResults([instring[start:loc]]) + results['original'] = self.match_string + results['mismatches'] = mismatches + return loc, results + + raise ParseException(instring, loc, self.errmsg, self) + + +class Word(Token): + """ + Token for matching words composed of allowed character sets. + Defined with string containing all allowed initial characters, + an optional string containing allowed body characters (if omitted, + defaults to the initial character set), and an optional minimum, + maximum, and/or exact length. The default value for C{min} is 1 (a + minimum value < 1 is not valid); the default values for C{max} and C{exact} + are 0, meaning no maximum or exact length restriction. An optional + C{excludeChars} parameter can list characters that might be found in + the input C{bodyChars} string; useful to define a word of all printables + except for one or two characters, for instance. + + L{srange} is useful for defining custom character set strings for defining + C{Word} expressions, using range notation from regular expression character sets. + + A common mistake is to use C{Word} to match a specific literal string, as in + C{Word("Address")}. Remember that C{Word} uses the string argument to define + I{sets} of matchable characters. This expression would match "Add", "AAA", + "dAred", or any other word made up of the characters 'A', 'd', 'r', 'e', and 's'. + To match an exact literal string, use L{Literal} or L{Keyword}. + + pyparsing includes helper strings for building Words: + - L{alphas} + - L{nums} + - L{alphanums} + - L{hexnums} + - L{alphas8bit} (alphabetic characters in ASCII range 128-255 - accented, tilded, umlauted, etc.) + - L{punc8bit} (non-alphabetic characters in ASCII range 128-255 - currency, symbols, superscripts, diacriticals, etc.) + - L{printables} (any non-whitespace character) + + Example:: + # a word composed of digits + integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9")) + + # a word with a leading capital, and zero or more lowercase + capital_word = Word(alphas.upper(), alphas.lower()) + + # hostnames are alphanumeric, with leading alpha, and '-' + hostname = Word(alphas, alphanums+'-') + + # roman numeral (not a strict parser, accepts invalid mix of characters) + roman = Word("IVXLCDM") + + # any string of non-whitespace characters, except for ',' + csv_value = Word(printables, excludeChars=",") + """ + def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None ): + super(Word,self).__init__() + if excludeChars: + initChars = ''.join(c for c in initChars if c not in excludeChars) + if bodyChars: + bodyChars = ''.join(c for c in bodyChars if c not in excludeChars) + self.initCharsOrig = initChars + self.initChars = set(initChars) + if bodyChars : + self.bodyCharsOrig = bodyChars + self.bodyChars = set(bodyChars) + else: + self.bodyCharsOrig = initChars + self.bodyChars = set(initChars) + + self.maxSpecified = max > 0 + + if min < 1: + raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted") + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayIndexError = False + self.asKeyword = asKeyword + + if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0): + if self.bodyCharsOrig == self.initCharsOrig: + self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig) + elif len(self.initCharsOrig) == 1: + self.reString = "%s[%s]*" % \ + (re.escape(self.initCharsOrig), + _escapeRegexRangeChars(self.bodyCharsOrig),) + else: + self.reString = "[%s][%s]*" % \ + (_escapeRegexRangeChars(self.initCharsOrig), + _escapeRegexRangeChars(self.bodyCharsOrig),) + if self.asKeyword: + self.reString = r"\b"+self.reString+r"\b" + try: + self.re = re.compile( self.reString ) + except Exception: + self.re = None + + def parseImpl( self, instring, loc, doActions=True ): + if self.re: + result = self.re.match(instring,loc) + if not result: + raise ParseException(instring, loc, self.errmsg, self) + + loc = result.end() + return loc, result.group() + + if not(instring[ loc ] in self.initChars): + raise ParseException(instring, loc, self.errmsg, self) + + start = loc + loc += 1 + instrlen = len(instring) + bodychars = self.bodyChars + maxloc = start + self.maxLen + maxloc = min( maxloc, instrlen ) + while loc < maxloc and instring[loc] in bodychars: + loc += 1 + + throwException = False + if loc - start < self.minLen: + throwException = True + if self.maxSpecified and loc < instrlen and instring[loc] in bodychars: + throwException = True + if self.asKeyword: + if (start>0 and instring[start-1] in bodychars) or (loc<instrlen and instring[loc] in bodychars): + throwException = True + + if throwException: + raise ParseException(instring, loc, self.errmsg, self) + + return loc, instring[start:loc] + + def __str__( self ): + try: + return super(Word,self).__str__() + except Exception: + pass + + + if self.strRepr is None: + + def charsAsStr(s): + if len(s)>4: + return s[:4]+"..." + else: + return s + + if ( self.initCharsOrig != self.bodyCharsOrig ): + self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) ) + else: + self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig) + + return self.strRepr + + +class Regex(Token): + r""" + Token for matching strings that match a given regular expression. + Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module. + If the given regex contains named groups (defined using C{(?P<name>...)}), these will be preserved as + named parse results. + + Example:: + realnum = Regex(r"[+-]?\d+\.\d*") + date = Regex(r'(?P<year>\d{4})-(?P<month>\d\d?)-(?P<day>\d\d?)') + # ref: http://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression + roman = Regex(r"M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})") + """ + compiledREtype = type(re.compile("[A-Z]")) + def __init__( self, pattern, flags=0): + """The parameters C{pattern} and C{flags} are passed to the C{re.compile()} function as-is. See the Python C{re} module for an explanation of the acceptable patterns and flags.""" + super(Regex,self).__init__() + + if isinstance(pattern, basestring): + if not pattern: + warnings.warn("null string passed to Regex; use Empty() instead", + SyntaxWarning, stacklevel=2) + + self.pattern = pattern + self.flags = flags + + try: + self.re = re.compile(self.pattern, self.flags) + self.reString = self.pattern + except sre_constants.error: + warnings.warn("invalid pattern (%s) passed to Regex" % pattern, + SyntaxWarning, stacklevel=2) + raise + + elif isinstance(pattern, Regex.compiledREtype): + self.re = pattern + self.pattern = \ + self.reString = str(pattern) + self.flags = flags + + else: + raise ValueError("Regex may only be constructed with a string or a compiled RE object") + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayIndexError = False + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + result = self.re.match(instring,loc) + if not result: + raise ParseException(instring, loc, self.errmsg, self) + + loc = result.end() + d = result.groupdict() + ret = ParseResults(result.group()) + if d: + for k in d: + ret[k] = d[k] + return loc,ret + + def __str__( self ): + try: + return super(Regex,self).__str__() + except Exception: + pass + + if self.strRepr is None: + self.strRepr = "Re:(%s)" % repr(self.pattern) + + return self.strRepr + + +class QuotedString(Token): + r""" + Token for matching strings that are delimited by quoting characters. + + Defined with the following parameters: + - quoteChar - string of one or more characters defining the quote delimiting string + - escChar - character to escape quotes, typically backslash (default=C{None}) + - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=C{None}) + - multiline - boolean indicating whether quotes can span multiple lines (default=C{False}) + - unquoteResults - boolean indicating whether the matched text should be unquoted (default=C{True}) + - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=C{None} => same as quoteChar) + - convertWhitespaceEscapes - convert escaped whitespace (C{'\t'}, C{'\n'}, etc.) to actual whitespace (default=C{True}) + + Example:: + qs = QuotedString('"') + print(qs.searchString('lsjdf "This is the quote" sldjf')) + complex_qs = QuotedString('{{', endQuoteChar='}}') + print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf')) + sql_qs = QuotedString('"', escQuote='""') + print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf')) + prints:: + [['This is the quote']] + [['This is the "quote"']] + [['This is the quote with "embedded" quotes']] + """ + def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True): + super(QuotedString,self).__init__() + + # remove white space from quote chars - wont work anyway + quoteChar = quoteChar.strip() + if not quoteChar: + warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) + raise SyntaxError() + + if endQuoteChar is None: + endQuoteChar = quoteChar + else: + endQuoteChar = endQuoteChar.strip() + if not endQuoteChar: + warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) + raise SyntaxError() + + self.quoteChar = quoteChar + self.quoteCharLen = len(quoteChar) + self.firstQuoteChar = quoteChar[0] + self.endQuoteChar = endQuoteChar + self.endQuoteCharLen = len(endQuoteChar) + self.escChar = escChar + self.escQuote = escQuote + self.unquoteResults = unquoteResults + self.convertWhitespaceEscapes = convertWhitespaceEscapes + + if multiline: + self.flags = re.MULTILINE | re.DOTALL + self.pattern = r'%s(?:[^%s%s]' % \ + ( re.escape(self.quoteChar), + _escapeRegexRangeChars(self.endQuoteChar[0]), + (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) + else: + self.flags = 0 + self.pattern = r'%s(?:[^%s\n\r%s]' % \ + ( re.escape(self.quoteChar), + _escapeRegexRangeChars(self.endQuoteChar[0]), + (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) + if len(self.endQuoteChar) > 1: + self.pattern += ( + '|(?:' + ')|(?:'.join("%s[^%s]" % (re.escape(self.endQuoteChar[:i]), + _escapeRegexRangeChars(self.endQuoteChar[i])) + for i in range(len(self.endQuoteChar)-1,0,-1)) + ')' + ) + if escQuote: + self.pattern += (r'|(?:%s)' % re.escape(escQuote)) + if escChar: + self.pattern += (r'|(?:%s.)' % re.escape(escChar)) + self.escCharReplacePattern = re.escape(self.escChar)+"(.)" + self.pattern += (r')*%s' % re.escape(self.endQuoteChar)) + + try: + self.re = re.compile(self.pattern, self.flags) + self.reString = self.pattern + except sre_constants.error: + warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern, + SyntaxWarning, stacklevel=2) + raise + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayIndexError = False + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None + if not result: + raise ParseException(instring, loc, self.errmsg, self) + + loc = result.end() + ret = result.group() + + if self.unquoteResults: + + # strip off quotes + ret = ret[self.quoteCharLen:-self.endQuoteCharLen] + + if isinstance(ret,basestring): + # replace escaped whitespace + if '\\' in ret and self.convertWhitespaceEscapes: + ws_map = { + r'\t' : '\t', + r'\n' : '\n', + r'\f' : '\f', + r'\r' : '\r', + } + for wslit,wschar in ws_map.items(): + ret = ret.replace(wslit, wschar) + + # replace escaped characters + if self.escChar: + ret = re.sub(self.escCharReplacePattern, r"\g<1>", ret) + + # replace escaped quotes + if self.escQuote: + ret = ret.replace(self.escQuote, self.endQuoteChar) + + return loc, ret + + def __str__( self ): + try: + return super(QuotedString,self).__str__() + except Exception: + pass + + if self.strRepr is None: + self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar) + + return self.strRepr + + +class CharsNotIn(Token): + """ + Token for matching words composed of characters I{not} in a given set (will + include whitespace in matched characters if not listed in the provided exclusion set - see example). + Defined with string containing all disallowed characters, and an optional + minimum, maximum, and/or exact length. The default value for C{min} is 1 (a + minimum value < 1 is not valid); the default values for C{max} and C{exact} + are 0, meaning no maximum or exact length restriction. + + Example:: + # define a comma-separated-value as anything that is not a ',' + csv_value = CharsNotIn(',') + print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213")) + prints:: + ['dkls', 'lsdkjf', 's12 34', '@!#', '213'] + """ + def __init__( self, notChars, min=1, max=0, exact=0 ): + super(CharsNotIn,self).__init__() + self.skipWhitespace = False + self.notChars = notChars + + if min < 1: + raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted") + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = ( self.minLen == 0 ) + self.mayIndexError = False + + def parseImpl( self, instring, loc, doActions=True ): + if instring[loc] in self.notChars: + raise ParseException(instring, loc, self.errmsg, self) + + start = loc + loc += 1 + notchars = self.notChars + maxlen = min( start+self.maxLen, len(instring) ) + while loc < maxlen and \ + (instring[loc] not in notchars): + loc += 1 + + if loc - start < self.minLen: + raise ParseException(instring, loc, self.errmsg, self) + + return loc, instring[start:loc] + + def __str__( self ): + try: + return super(CharsNotIn, self).__str__() + except Exception: + pass + + if self.strRepr is None: + if len(self.notChars) > 4: + self.strRepr = "!W:(%s...)" % self.notChars[:4] + else: + self.strRepr = "!W:(%s)" % self.notChars + + return self.strRepr + +class White(Token): + """ + Special matching class for matching whitespace. Normally, whitespace is ignored + by pyparsing grammars. This class is included when some whitespace structures + are significant. Define with a string containing the whitespace characters to be + matched; default is C{" \\t\\r\\n"}. Also takes optional C{min}, C{max}, and C{exact} arguments, + as defined for the C{L{Word}} class. + """ + whiteStrs = { + " " : "<SPC>", + "\t": "<TAB>", + "\n": "<LF>", + "\r": "<CR>", + "\f": "<FF>", + } + def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0): + super(White,self).__init__() + self.matchWhite = ws + self.setWhitespaceChars( "".join(c for c in self.whiteChars if c not in self.matchWhite) ) + #~ self.leaveWhitespace() + self.name = ("".join(White.whiteStrs[c] for c in self.matchWhite)) + self.mayReturnEmpty = True + self.errmsg = "Expected " + self.name + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + def parseImpl( self, instring, loc, doActions=True ): + if not(instring[ loc ] in self.matchWhite): + raise ParseException(instring, loc, self.errmsg, self) + start = loc + loc += 1 + maxloc = start + self.maxLen + maxloc = min( maxloc, len(instring) ) + while loc < maxloc and instring[loc] in self.matchWhite: + loc += 1 + + if loc - start < self.minLen: + raise ParseException(instring, loc, self.errmsg, self) + + return loc, instring[start:loc] + + +class _PositionToken(Token): + def __init__( self ): + super(_PositionToken,self).__init__() + self.name=self.__class__.__name__ + self.mayReturnEmpty = True + self.mayIndexError = False + +class GoToColumn(_PositionToken): + """ + Token to advance to a specific column of input text; useful for tabular report scraping. + """ + def __init__( self, colno ): + super(GoToColumn,self).__init__() + self.col = colno + + def preParse( self, instring, loc ): + if col(loc,instring) != self.col: + instrlen = len(instring) + if self.ignoreExprs: + loc = self._skipIgnorables( instring, loc ) + while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col : + loc += 1 + return loc + + def parseImpl( self, instring, loc, doActions=True ): + thiscol = col( loc, instring ) + if thiscol > self.col: + raise ParseException( instring, loc, "Text not in expected column", self ) + newloc = loc + self.col - thiscol + ret = instring[ loc: newloc ] + return newloc, ret + + +class LineStart(_PositionToken): + """ + Matches if current position is at the beginning of a line within the parse string + + Example:: + + test = '''\ + AAA this line + AAA and this line + AAA but not this one + B AAA and definitely not this one + ''' + + for t in (LineStart() + 'AAA' + restOfLine).searchString(test): + print(t) + + Prints:: + ['AAA', ' this line'] + ['AAA', ' and this line'] + + """ + def __init__( self ): + super(LineStart,self).__init__() + self.errmsg = "Expected start of line" + + def parseImpl( self, instring, loc, doActions=True ): + if col(loc, instring) == 1: + return loc, [] + raise ParseException(instring, loc, self.errmsg, self) + +class LineEnd(_PositionToken): + """ + Matches if current position is at the end of a line within the parse string + """ + def __init__( self ): + super(LineEnd,self).__init__() + self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) + self.errmsg = "Expected end of line" + + def parseImpl( self, instring, loc, doActions=True ): + if loc<len(instring): + if instring[loc] == "\n": + return loc+1, "\n" + else: + raise ParseException(instring, loc, self.errmsg, self) + elif loc == len(instring): + return loc+1, [] + else: + raise ParseException(instring, loc, self.errmsg, self) + +class StringStart(_PositionToken): + """ + Matches if current position is at the beginning of the parse string + """ + def __init__( self ): + super(StringStart,self).__init__() + self.errmsg = "Expected start of text" + + def parseImpl( self, instring, loc, doActions=True ): + if loc != 0: + # see if entire string up to here is just whitespace and ignoreables + if loc != self.preParse( instring, 0 ): + raise ParseException(instring, loc, self.errmsg, self) + return loc, [] + +class StringEnd(_PositionToken): + """ + Matches if current position is at the end of the parse string + """ + def __init__( self ): + super(StringEnd,self).__init__() + self.errmsg = "Expected end of text" + + def parseImpl( self, instring, loc, doActions=True ): + if loc < len(instring): + raise ParseException(instring, loc, self.errmsg, self) + elif loc == len(instring): + return loc+1, [] + elif loc > len(instring): + return loc, [] + else: + raise ParseException(instring, loc, self.errmsg, self) + +class WordStart(_PositionToken): + """ + Matches if the current position is at the beginning of a Word, and + is not preceded by any character in a given set of C{wordChars} + (default=C{printables}). To emulate the C{\b} behavior of regular expressions, + use C{WordStart(alphanums)}. C{WordStart} will also match at the beginning of + the string being parsed, or at the beginning of a line. + """ + def __init__(self, wordChars = printables): + super(WordStart,self).__init__() + self.wordChars = set(wordChars) + self.errmsg = "Not at the start of a word" + + def parseImpl(self, instring, loc, doActions=True ): + if loc != 0: + if (instring[loc-1] in self.wordChars or + instring[loc] not in self.wordChars): + raise ParseException(instring, loc, self.errmsg, self) + return loc, [] + +class WordEnd(_PositionToken): + """ + Matches if the current position is at the end of a Word, and + is not followed by any character in a given set of C{wordChars} + (default=C{printables}). To emulate the C{\b} behavior of regular expressions, + use C{WordEnd(alphanums)}. C{WordEnd} will also match at the end of + the string being parsed, or at the end of a line. + """ + def __init__(self, wordChars = printables): + super(WordEnd,self).__init__() + self.wordChars = set(wordChars) + self.skipWhitespace = False + self.errmsg = "Not at the end of a word" + + def parseImpl(self, instring, loc, doActions=True ): + instrlen = len(instring) + if instrlen>0 and loc<instrlen: + if (instring[loc] in self.wordChars or + instring[loc-1] not in self.wordChars): + raise ParseException(instring, loc, self.errmsg, self) + return loc, [] + + +class ParseExpression(ParserElement): + """ + Abstract subclass of ParserElement, for combining and post-processing parsed tokens. + """ + def __init__( self, exprs, savelist = False ): + super(ParseExpression,self).__init__(savelist) + if isinstance( exprs, _generatorType ): + exprs = list(exprs) + + if isinstance( exprs, basestring ): + self.exprs = [ ParserElement._literalStringClass( exprs ) ] + elif isinstance( exprs, Iterable ): + exprs = list(exprs) + # if sequence of strings provided, wrap with Literal + if all(isinstance(expr, basestring) for expr in exprs): + exprs = map(ParserElement._literalStringClass, exprs) + self.exprs = list(exprs) + else: + try: + self.exprs = list( exprs ) + except TypeError: + self.exprs = [ exprs ] + self.callPreparse = False + + def __getitem__( self, i ): + return self.exprs[i] + + def append( self, other ): + self.exprs.append( other ) + self.strRepr = None + return self + + def leaveWhitespace( self ): + """Extends C{leaveWhitespace} defined in base class, and also invokes C{leaveWhitespace} on + all contained expressions.""" + self.skipWhitespace = False + self.exprs = [ e.copy() for e in self.exprs ] + for e in self.exprs: + e.leaveWhitespace() + return self + + def ignore( self, other ): + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + super( ParseExpression, self).ignore( other ) + for e in self.exprs: + e.ignore( self.ignoreExprs[-1] ) + else: + super( ParseExpression, self).ignore( other ) + for e in self.exprs: + e.ignore( self.ignoreExprs[-1] ) + return self + + def __str__( self ): + try: + return super(ParseExpression,self).__str__() + except Exception: + pass + + if self.strRepr is None: + self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.exprs) ) + return self.strRepr + + def streamline( self ): + super(ParseExpression,self).streamline() + + for e in self.exprs: + e.streamline() + + # collapse nested And's of the form And( And( And( a,b), c), d) to And( a,b,c,d ) + # but only if there are no parse actions or resultsNames on the nested And's + # (likewise for Or's and MatchFirst's) + if ( len(self.exprs) == 2 ): + other = self.exprs[0] + if ( isinstance( other, self.__class__ ) and + not(other.parseAction) and + other.resultsName is None and + not other.debug ): + self.exprs = other.exprs[:] + [ self.exprs[1] ] + self.strRepr = None + self.mayReturnEmpty |= other.mayReturnEmpty + self.mayIndexError |= other.mayIndexError + + other = self.exprs[-1] + if ( isinstance( other, self.__class__ ) and + not(other.parseAction) and + other.resultsName is None and + not other.debug ): + self.exprs = self.exprs[:-1] + other.exprs[:] + self.strRepr = None + self.mayReturnEmpty |= other.mayReturnEmpty + self.mayIndexError |= other.mayIndexError + + self.errmsg = "Expected " + _ustr(self) + + return self + + def setResultsName( self, name, listAllMatches=False ): + ret = super(ParseExpression,self).setResultsName(name,listAllMatches) + return ret + + def validate( self, validateTrace=[] ): + tmp = validateTrace[:]+[self] + for e in self.exprs: + e.validate(tmp) + self.checkRecursion( [] ) + + def copy(self): + ret = super(ParseExpression,self).copy() + ret.exprs = [e.copy() for e in self.exprs] + return ret + +class And(ParseExpression): + """ + Requires all given C{ParseExpression}s to be found in the given order. + Expressions may be separated by whitespace. + May be constructed using the C{'+'} operator. + May also be constructed using the C{'-'} operator, which will suppress backtracking. + + Example:: + integer = Word(nums) + name_expr = OneOrMore(Word(alphas)) + + expr = And([integer("id"),name_expr("name"),integer("age")]) + # more easily written as: + expr = integer("id") + name_expr("name") + integer("age") + """ + + class _ErrorStop(Empty): + def __init__(self, *args, **kwargs): + super(And._ErrorStop,self).__init__(*args, **kwargs) + self.name = '-' + self.leaveWhitespace() + + def __init__( self, exprs, savelist = True ): + super(And,self).__init__(exprs, savelist) + self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) + self.setWhitespaceChars( self.exprs[0].whiteChars ) + self.skipWhitespace = self.exprs[0].skipWhitespace + self.callPreparse = True + + def parseImpl( self, instring, loc, doActions=True ): + # pass False as last arg to _parse for first element, since we already + # pre-parsed the string as part of our And pre-parsing + loc, resultlist = self.exprs[0]._parse( instring, loc, doActions, callPreParse=False ) + errorStop = False + for e in self.exprs[1:]: + if isinstance(e, And._ErrorStop): + errorStop = True + continue + if errorStop: + try: + loc, exprtokens = e._parse( instring, loc, doActions ) + except ParseSyntaxException: + raise + except ParseBaseException as pe: + pe.__traceback__ = None + raise ParseSyntaxException._from_exception(pe) + except IndexError: + raise ParseSyntaxException(instring, len(instring), self.errmsg, self) + else: + loc, exprtokens = e._parse( instring, loc, doActions ) + if exprtokens or exprtokens.haskeys(): + resultlist += exprtokens + return loc, resultlist + + def __iadd__(self, other ): + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + return self.append( other ) #And( [ self, other ] ) + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + if not e.mayReturnEmpty: + break + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " ".join(_ustr(e) for e in self.exprs) + "}" + + return self.strRepr + + +class Or(ParseExpression): + """ + Requires that at least one C{ParseExpression} is found. + If two expressions match, the expression that matches the longest string will be used. + May be constructed using the C{'^'} operator. + + Example:: + # construct Or using '^' operator + + number = Word(nums) ^ Combine(Word(nums) + '.' + Word(nums)) + print(number.searchString("123 3.1416 789")) + prints:: + [['123'], ['3.1416'], ['789']] + """ + def __init__( self, exprs, savelist = False ): + super(Or,self).__init__(exprs, savelist) + if self.exprs: + self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) + else: + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + maxExcLoc = -1 + maxException = None + matches = [] + for e in self.exprs: + try: + loc2 = e.tryParse( instring, loc ) + except ParseException as err: + err.__traceback__ = None + if err.loc > maxExcLoc: + maxException = err + maxExcLoc = err.loc + except IndexError: + if len(instring) > maxExcLoc: + maxException = ParseException(instring,len(instring),e.errmsg,self) + maxExcLoc = len(instring) + else: + # save match among all matches, to retry longest to shortest + matches.append((loc2, e)) + + if matches: + matches.sort(key=lambda x: -x[0]) + for _,e in matches: + try: + return e._parse( instring, loc, doActions ) + except ParseException as err: + err.__traceback__ = None + if err.loc > maxExcLoc: + maxException = err + maxExcLoc = err.loc + + if maxException is not None: + maxException.msg = self.errmsg + raise maxException + else: + raise ParseException(instring, loc, "no defined alternatives to match", self) + + + def __ixor__(self, other ): + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + return self.append( other ) #Or( [ self, other ] ) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " ^ ".join(_ustr(e) for e in self.exprs) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class MatchFirst(ParseExpression): + """ + Requires that at least one C{ParseExpression} is found. + If two expressions match, the first one listed is the one that will match. + May be constructed using the C{'|'} operator. + + Example:: + # construct MatchFirst using '|' operator + + # watch the order of expressions to match + number = Word(nums) | Combine(Word(nums) + '.' + Word(nums)) + print(number.searchString("123 3.1416 789")) # Fail! -> [['123'], ['3'], ['1416'], ['789']] + + # put more selective expression first + number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums) + print(number.searchString("123 3.1416 789")) # Better -> [['123'], ['3.1416'], ['789']] + """ + def __init__( self, exprs, savelist = False ): + super(MatchFirst,self).__init__(exprs, savelist) + if self.exprs: + self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) + else: + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + maxExcLoc = -1 + maxException = None + for e in self.exprs: + try: + ret = e._parse( instring, loc, doActions ) + return ret + except ParseException as err: + if err.loc > maxExcLoc: + maxException = err + maxExcLoc = err.loc + except IndexError: + if len(instring) > maxExcLoc: + maxException = ParseException(instring,len(instring),e.errmsg,self) + maxExcLoc = len(instring) + + # only got here if no expression matched, raise exception for match that made it the furthest + else: + if maxException is not None: + maxException.msg = self.errmsg + raise maxException + else: + raise ParseException(instring, loc, "no defined alternatives to match", self) + + def __ior__(self, other ): + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + return self.append( other ) #MatchFirst( [ self, other ] ) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " | ".join(_ustr(e) for e in self.exprs) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class Each(ParseExpression): + """ + Requires all given C{ParseExpression}s to be found, but in any order. + Expressions may be separated by whitespace. + May be constructed using the C{'&'} operator. + + Example:: + color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN") + shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON") + integer = Word(nums) + shape_attr = "shape:" + shape_type("shape") + posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn") + color_attr = "color:" + color("color") + size_attr = "size:" + integer("size") + + # use Each (using operator '&') to accept attributes in any order + # (shape and posn are required, color and size are optional) + shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr) + + shape_spec.runTests(''' + shape: SQUARE color: BLACK posn: 100, 120 + shape: CIRCLE size: 50 color: BLUE posn: 50,80 + color:GREEN size:20 shape:TRIANGLE posn:20,40 + ''' + ) + prints:: + shape: SQUARE color: BLACK posn: 100, 120 + ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']] + - color: BLACK + - posn: ['100', ',', '120'] + - x: 100 + - y: 120 + - shape: SQUARE + + + shape: CIRCLE size: 50 color: BLUE posn: 50,80 + ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']] + - color: BLUE + - posn: ['50', ',', '80'] + - x: 50 + - y: 80 + - shape: CIRCLE + - size: 50 + + + color: GREEN size: 20 shape: TRIANGLE posn: 20,40 + ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']] + - color: GREEN + - posn: ['20', ',', '40'] + - x: 20 + - y: 40 + - shape: TRIANGLE + - size: 20 + """ + def __init__( self, exprs, savelist = True ): + super(Each,self).__init__(exprs, savelist) + self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) + self.skipWhitespace = True + self.initExprGroups = True + + def parseImpl( self, instring, loc, doActions=True ): + if self.initExprGroups: + self.opt1map = dict((id(e.expr),e) for e in self.exprs if isinstance(e,Optional)) + opt1 = [ e.expr for e in self.exprs if isinstance(e,Optional) ] + opt2 = [ e for e in self.exprs if e.mayReturnEmpty and not isinstance(e,Optional)] + self.optionals = opt1 + opt2 + self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ] + self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ] + self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ] + self.required += self.multirequired + self.initExprGroups = False + tmpLoc = loc + tmpReqd = self.required[:] + tmpOpt = self.optionals[:] + matchOrder = [] + + keepMatching = True + while keepMatching: + tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired + failed = [] + for e in tmpExprs: + try: + tmpLoc = e.tryParse( instring, tmpLoc ) + except ParseException: + failed.append(e) + else: + matchOrder.append(self.opt1map.get(id(e),e)) + if e in tmpReqd: + tmpReqd.remove(e) + elif e in tmpOpt: + tmpOpt.remove(e) + if len(failed) == len(tmpExprs): + keepMatching = False + + if tmpReqd: + missing = ", ".join(_ustr(e) for e in tmpReqd) + raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing ) + + # add any unmatched Optionals, in case they have default values defined + matchOrder += [e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt] + + resultlist = [] + for e in matchOrder: + loc,results = e._parse(instring,loc,doActions) + resultlist.append(results) + + finalResults = sum(resultlist, ParseResults([])) + return loc, finalResults + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " & ".join(_ustr(e) for e in self.exprs) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class ParseElementEnhance(ParserElement): + """ + Abstract subclass of C{ParserElement}, for combining and post-processing parsed tokens. + """ + def __init__( self, expr, savelist=False ): + super(ParseElementEnhance,self).__init__(savelist) + if isinstance( expr, basestring ): + if issubclass(ParserElement._literalStringClass, Token): + expr = ParserElement._literalStringClass(expr) + else: + expr = ParserElement._literalStringClass(Literal(expr)) + self.expr = expr + self.strRepr = None + if expr is not None: + self.mayIndexError = expr.mayIndexError + self.mayReturnEmpty = expr.mayReturnEmpty + self.setWhitespaceChars( expr.whiteChars ) + self.skipWhitespace = expr.skipWhitespace + self.saveAsList = expr.saveAsList + self.callPreparse = expr.callPreparse + self.ignoreExprs.extend(expr.ignoreExprs) + + def parseImpl( self, instring, loc, doActions=True ): + if self.expr is not None: + return self.expr._parse( instring, loc, doActions, callPreParse=False ) + else: + raise ParseException("",loc,self.errmsg,self) + + def leaveWhitespace( self ): + self.skipWhitespace = False + self.expr = self.expr.copy() + if self.expr is not None: + self.expr.leaveWhitespace() + return self + + def ignore( self, other ): + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + super( ParseElementEnhance, self).ignore( other ) + if self.expr is not None: + self.expr.ignore( self.ignoreExprs[-1] ) + else: + super( ParseElementEnhance, self).ignore( other ) + if self.expr is not None: + self.expr.ignore( self.ignoreExprs[-1] ) + return self + + def streamline( self ): + super(ParseElementEnhance,self).streamline() + if self.expr is not None: + self.expr.streamline() + return self + + def checkRecursion( self, parseElementList ): + if self in parseElementList: + raise RecursiveGrammarException( parseElementList+[self] ) + subRecCheckList = parseElementList[:] + [ self ] + if self.expr is not None: + self.expr.checkRecursion( subRecCheckList ) + + def validate( self, validateTrace=[] ): + tmp = validateTrace[:]+[self] + if self.expr is not None: + self.expr.validate(tmp) + self.checkRecursion( [] ) + + def __str__( self ): + try: + return super(ParseElementEnhance,self).__str__() + except Exception: + pass + + if self.strRepr is None and self.expr is not None: + self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) ) + return self.strRepr + + +class FollowedBy(ParseElementEnhance): + """ + Lookahead matching of the given parse expression. C{FollowedBy} + does I{not} advance the parsing position within the input string, it only + verifies that the specified parse expression matches at the current + position. C{FollowedBy} always returns a null token list. + + Example:: + # use FollowedBy to match a label only if it is followed by a ':' + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + + OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint() + prints:: + [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']] + """ + def __init__( self, expr ): + super(FollowedBy,self).__init__(expr) + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + self.expr.tryParse( instring, loc ) + return loc, [] + + +class NotAny(ParseElementEnhance): + """ + Lookahead to disallow matching with the given parse expression. C{NotAny} + does I{not} advance the parsing position within the input string, it only + verifies that the specified parse expression does I{not} match at the current + position. Also, C{NotAny} does I{not} skip over leading whitespace. C{NotAny} + always returns a null token list. May be constructed using the '~' operator. + + Example:: + + """ + def __init__( self, expr ): + super(NotAny,self).__init__(expr) + #~ self.leaveWhitespace() + self.skipWhitespace = False # do NOT use self.leaveWhitespace(), don't want to propagate to exprs + self.mayReturnEmpty = True + self.errmsg = "Found unwanted token, "+_ustr(self.expr) + + def parseImpl( self, instring, loc, doActions=True ): + if self.expr.canParseNext(instring, loc): + raise ParseException(instring, loc, self.errmsg, self) + return loc, [] + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "~{" + _ustr(self.expr) + "}" + + return self.strRepr + +class _MultipleMatch(ParseElementEnhance): + def __init__( self, expr, stopOn=None): + super(_MultipleMatch, self).__init__(expr) + self.saveAsList = True + ender = stopOn + if isinstance(ender, basestring): + ender = ParserElement._literalStringClass(ender) + self.not_ender = ~ender if ender is not None else None + + def parseImpl( self, instring, loc, doActions=True ): + self_expr_parse = self.expr._parse + self_skip_ignorables = self._skipIgnorables + check_ender = self.not_ender is not None + if check_ender: + try_not_ender = self.not_ender.tryParse + + # must be at least one (but first see if we are the stopOn sentinel; + # if so, fail) + if check_ender: + try_not_ender(instring, loc) + loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False ) + try: + hasIgnoreExprs = (not not self.ignoreExprs) + while 1: + if check_ender: + try_not_ender(instring, loc) + if hasIgnoreExprs: + preloc = self_skip_ignorables( instring, loc ) + else: + preloc = loc + loc, tmptokens = self_expr_parse( instring, preloc, doActions ) + if tmptokens or tmptokens.haskeys(): + tokens += tmptokens + except (ParseException,IndexError): + pass + + return loc, tokens + +class OneOrMore(_MultipleMatch): + """ + Repetition of one or more of the given expression. + + Parameters: + - expr - expression that must match one or more times + - stopOn - (default=C{None}) - expression for a terminating sentinel + (only required if the sentinel would ordinarily match the repetition + expression) + + Example:: + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) + + text = "shape: SQUARE posn: upper left color: BLACK" + OneOrMore(attr_expr).parseString(text).pprint() # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']] + + # use stopOn attribute for OneOrMore to avoid reading label string as part of the data + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']] + + # could also be written as + (attr_expr * (1,)).parseString(text).pprint() + """ + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + _ustr(self.expr) + "}..." + + return self.strRepr + +class ZeroOrMore(_MultipleMatch): + """ + Optional repetition of zero or more of the given expression. + + Parameters: + - expr - expression that must match zero or more times + - stopOn - (default=C{None}) - expression for a terminating sentinel + (only required if the sentinel would ordinarily match the repetition + expression) + + Example: similar to L{OneOrMore} + """ + def __init__( self, expr, stopOn=None): + super(ZeroOrMore,self).__init__(expr, stopOn=stopOn) + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + try: + return super(ZeroOrMore, self).parseImpl(instring, loc, doActions) + except (ParseException,IndexError): + return loc, [] + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "[" + _ustr(self.expr) + "]..." + + return self.strRepr + +class _NullToken(object): + def __bool__(self): + return False + __nonzero__ = __bool__ + def __str__(self): + return "" + +_optionalNotMatched = _NullToken() +class Optional(ParseElementEnhance): + """ + Optional matching of the given expression. + + Parameters: + - expr - expression that must match zero or more times + - default (optional) - value to be returned if the optional expression is not found. + + Example:: + # US postal code can be a 5-digit zip, plus optional 4-digit qualifier + zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4))) + zip.runTests(''' + # traditional ZIP code + 12345 + + # ZIP+4 form + 12101-0001 + + # invalid ZIP + 98765- + ''') + prints:: + # traditional ZIP code + 12345 + ['12345'] + + # ZIP+4 form + 12101-0001 + ['12101-0001'] + + # invalid ZIP + 98765- + ^ + FAIL: Expected end of text (at char 5), (line:1, col:6) + """ + def __init__( self, expr, default=_optionalNotMatched ): + super(Optional,self).__init__( expr, savelist=False ) + self.saveAsList = self.expr.saveAsList + self.defaultValue = default + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + try: + loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) + except (ParseException,IndexError): + if self.defaultValue is not _optionalNotMatched: + if self.expr.resultsName: + tokens = ParseResults([ self.defaultValue ]) + tokens[self.expr.resultsName] = self.defaultValue + else: + tokens = [ self.defaultValue ] + else: + tokens = [] + return loc, tokens + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "[" + _ustr(self.expr) + "]" + + return self.strRepr + +class SkipTo(ParseElementEnhance): + """ + Token for skipping over all undefined text until the matched expression is found. + + Parameters: + - expr - target expression marking the end of the data to be skipped + - include - (default=C{False}) if True, the target expression is also parsed + (the skipped text and target expression are returned as a 2-element list). + - ignore - (default=C{None}) used to define grammars (typically quoted strings and + comments) that might contain false matches to the target expression + - failOn - (default=C{None}) define expressions that are not allowed to be + included in the skipped test; if found before the target expression is found, + the SkipTo is not a match + + Example:: + report = ''' + Outstanding Issues Report - 1 Jan 2000 + + # | Severity | Description | Days Open + -----+----------+-------------------------------------------+----------- + 101 | Critical | Intermittent system crash | 6 + 94 | Cosmetic | Spelling error on Login ('log|n') | 14 + 79 | Minor | System slow when running too many reports | 47 + ''' + integer = Word(nums) + SEP = Suppress('|') + # use SkipTo to simply match everything up until the next SEP + # - ignore quoted strings, so that a '|' character inside a quoted string does not match + # - parse action will call token.strip() for each matched token, i.e., the description body + string_data = SkipTo(SEP, ignore=quotedString) + string_data.setParseAction(tokenMap(str.strip)) + ticket_expr = (integer("issue_num") + SEP + + string_data("sev") + SEP + + string_data("desc") + SEP + + integer("days_open")) + + for tkt in ticket_expr.searchString(report): + print tkt.dump() + prints:: + ['101', 'Critical', 'Intermittent system crash', '6'] + - days_open: 6 + - desc: Intermittent system crash + - issue_num: 101 + - sev: Critical + ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14'] + - days_open: 14 + - desc: Spelling error on Login ('log|n') + - issue_num: 94 + - sev: Cosmetic + ['79', 'Minor', 'System slow when running too many reports', '47'] + - days_open: 47 + - desc: System slow when running too many reports + - issue_num: 79 + - sev: Minor + """ + def __init__( self, other, include=False, ignore=None, failOn=None ): + super( SkipTo, self ).__init__( other ) + self.ignoreExpr = ignore + self.mayReturnEmpty = True + self.mayIndexError = False + self.includeMatch = include + self.asList = False + if isinstance(failOn, basestring): + self.failOn = ParserElement._literalStringClass(failOn) + else: + self.failOn = failOn + self.errmsg = "No match found for "+_ustr(self.expr) + + def parseImpl( self, instring, loc, doActions=True ): + startloc = loc + instrlen = len(instring) + expr = self.expr + expr_parse = self.expr._parse + self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None + self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None + + tmploc = loc + while tmploc <= instrlen: + if self_failOn_canParseNext is not None: + # break if failOn expression matches + if self_failOn_canParseNext(instring, tmploc): + break + + if self_ignoreExpr_tryParse is not None: + # advance past ignore expressions + while 1: + try: + tmploc = self_ignoreExpr_tryParse(instring, tmploc) + except ParseBaseException: + break + + try: + expr_parse(instring, tmploc, doActions=False, callPreParse=False) + except (ParseException, IndexError): + # no match, advance loc in string + tmploc += 1 + else: + # matched skipto expr, done + break + + else: + # ran off the end of the input string without matching skipto expr, fail + raise ParseException(instring, loc, self.errmsg, self) + + # build up return values + loc = tmploc + skiptext = instring[startloc:loc] + skipresult = ParseResults(skiptext) + + if self.includeMatch: + loc, mat = expr_parse(instring,loc,doActions,callPreParse=False) + skipresult += mat + + return loc, skipresult + +class Forward(ParseElementEnhance): + """ + Forward declaration of an expression to be defined later - + used for recursive grammars, such as algebraic infix notation. + When the expression is known, it is assigned to the C{Forward} variable using the '<<' operator. + + Note: take care when assigning to C{Forward} not to overlook precedence of operators. + Specifically, '|' has a lower precedence than '<<', so that:: + fwdExpr << a | b | c + will actually be evaluated as:: + (fwdExpr << a) | b | c + thereby leaving b and c out as parseable alternatives. It is recommended that you + explicitly group the values inserted into the C{Forward}:: + fwdExpr << (a | b | c) + Converting to use the '<<=' operator instead will avoid this problem. + + See L{ParseResults.pprint} for an example of a recursive parser created using + C{Forward}. + """ + def __init__( self, other=None ): + super(Forward,self).__init__( other, savelist=False ) + + def __lshift__( self, other ): + if isinstance( other, basestring ): + other = ParserElement._literalStringClass(other) + self.expr = other + self.strRepr = None + self.mayIndexError = self.expr.mayIndexError + self.mayReturnEmpty = self.expr.mayReturnEmpty + self.setWhitespaceChars( self.expr.whiteChars ) + self.skipWhitespace = self.expr.skipWhitespace + self.saveAsList = self.expr.saveAsList + self.ignoreExprs.extend(self.expr.ignoreExprs) + return self + + def __ilshift__(self, other): + return self << other + + def leaveWhitespace( self ): + self.skipWhitespace = False + return self + + def streamline( self ): + if not self.streamlined: + self.streamlined = True + if self.expr is not None: + self.expr.streamline() + return self + + def validate( self, validateTrace=[] ): + if self not in validateTrace: + tmp = validateTrace[:]+[self] + if self.expr is not None: + self.expr.validate(tmp) + self.checkRecursion([]) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + return self.__class__.__name__ + ": ..." + + # stubbed out for now - creates awful memory and perf issues + self._revertClass = self.__class__ + self.__class__ = _ForwardNoRecurse + try: + if self.expr is not None: + retString = _ustr(self.expr) + else: + retString = "None" + finally: + self.__class__ = self._revertClass + return self.__class__.__name__ + ": " + retString + + def copy(self): + if self.expr is not None: + return super(Forward,self).copy() + else: + ret = Forward() + ret <<= self + return ret + +class _ForwardNoRecurse(Forward): + def __str__( self ): + return "..." + +class TokenConverter(ParseElementEnhance): + """ + Abstract subclass of C{ParseExpression}, for converting parsed results. + """ + def __init__( self, expr, savelist=False ): + super(TokenConverter,self).__init__( expr )#, savelist ) + self.saveAsList = False + +class Combine(TokenConverter): + """ + Converter to concatenate all matching tokens to a single string. + By default, the matching patterns must also be contiguous in the input string; + this can be disabled by specifying C{'adjacent=False'} in the constructor. + + Example:: + real = Word(nums) + '.' + Word(nums) + print(real.parseString('3.1416')) # -> ['3', '.', '1416'] + # will also erroneously match the following + print(real.parseString('3. 1416')) # -> ['3', '.', '1416'] + + real = Combine(Word(nums) + '.' + Word(nums)) + print(real.parseString('3.1416')) # -> ['3.1416'] + # no match when there are internal spaces + print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...) + """ + def __init__( self, expr, joinString="", adjacent=True ): + super(Combine,self).__init__( expr ) + # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself + if adjacent: + self.leaveWhitespace() + self.adjacent = adjacent + self.skipWhitespace = True + self.joinString = joinString + self.callPreparse = True + + def ignore( self, other ): + if self.adjacent: + ParserElement.ignore(self, other) + else: + super( Combine, self).ignore( other ) + return self + + def postParse( self, instring, loc, tokenlist ): + retToks = tokenlist.copy() + del retToks[:] + retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults) + + if self.resultsName and retToks.haskeys(): + return [ retToks ] + else: + return retToks + +class Group(TokenConverter): + """ + Converter to return the matched tokens as a list - useful for returning tokens of C{L{ZeroOrMore}} and C{L{OneOrMore}} expressions. + + Example:: + ident = Word(alphas) + num = Word(nums) + term = ident | num + func = ident + Optional(delimitedList(term)) + print(func.parseString("fn a,b,100")) # -> ['fn', 'a', 'b', '100'] + + func = ident + Group(Optional(delimitedList(term))) + print(func.parseString("fn a,b,100")) # -> ['fn', ['a', 'b', '100']] + """ + def __init__( self, expr ): + super(Group,self).__init__( expr ) + self.saveAsList = True + + def postParse( self, instring, loc, tokenlist ): + return [ tokenlist ] + +class Dict(TokenConverter): + """ + Converter to return a repetitive expression as a list, but also as a dictionary. + Each element can also be referenced using the first token in the expression as its key. + Useful for tabular report scraping when the first column can be used as a item key. + + Example:: + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) + + text = "shape: SQUARE posn: upper left color: light blue texture: burlap" + attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + + # print attributes as plain groups + print(OneOrMore(attr_expr).parseString(text).dump()) + + # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names + result = Dict(OneOrMore(Group(attr_expr))).parseString(text) + print(result.dump()) + + # access named fields as dict entries, or output as dict + print(result['shape']) + print(result.asDict()) + prints:: + ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap'] + + [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] + - color: light blue + - posn: upper left + - shape: SQUARE + - texture: burlap + SQUARE + {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'} + See more examples at L{ParseResults} of accessing fields by results name. + """ + def __init__( self, expr ): + super(Dict,self).__init__( expr ) + self.saveAsList = True + + def postParse( self, instring, loc, tokenlist ): + for i,tok in enumerate(tokenlist): + if len(tok) == 0: + continue + ikey = tok[0] + if isinstance(ikey,int): + ikey = _ustr(tok[0]).strip() + if len(tok)==1: + tokenlist[ikey] = _ParseResultsWithOffset("",i) + elif len(tok)==2 and not isinstance(tok[1],ParseResults): + tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i) + else: + dictvalue = tok.copy() #ParseResults(i) + del dictvalue[0] + if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.haskeys()): + tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i) + else: + tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i) + + if self.resultsName: + return [ tokenlist ] + else: + return tokenlist + + +class Suppress(TokenConverter): + """ + Converter for ignoring the results of a parsed expression. + + Example:: + source = "a, b, c,d" + wd = Word(alphas) + wd_list1 = wd + ZeroOrMore(',' + wd) + print(wd_list1.parseString(source)) + + # often, delimiters that are useful during parsing are just in the + # way afterward - use Suppress to keep them out of the parsed output + wd_list2 = wd + ZeroOrMore(Suppress(',') + wd) + print(wd_list2.parseString(source)) + prints:: + ['a', ',', 'b', ',', 'c', ',', 'd'] + ['a', 'b', 'c', 'd'] + (See also L{delimitedList}.) + """ + def postParse( self, instring, loc, tokenlist ): + return [] + + def suppress( self ): + return self + + +class OnlyOnce(object): + """ + Wrapper for parse actions, to ensure they are only called once. + """ + def __init__(self, methodCall): + self.callable = _trim_arity(methodCall) + self.called = False + def __call__(self,s,l,t): + if not self.called: + results = self.callable(s,l,t) + self.called = True + return results + raise ParseException(s,l,"") + def reset(self): + self.called = False + +def traceParseAction(f): + """ + Decorator for debugging parse actions. + + When the parse action is called, this decorator will print C{">> entering I{method-name}(line:I{current_source_line}, I{parse_location}, I{matched_tokens})".} + When the parse action completes, the decorator will print C{"<<"} followed by the returned value, or any exception that the parse action raised. + + Example:: + wd = Word(alphas) + + @traceParseAction + def remove_duplicate_chars(tokens): + return ''.join(sorted(set(''.join(tokens)))) + + wds = OneOrMore(wd).setParseAction(remove_duplicate_chars) + print(wds.parseString("slkdjs sld sldd sdlf sdljf")) + prints:: + >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {})) + <<leaving remove_duplicate_chars (ret: 'dfjkls') + ['dfjkls'] + """ + f = _trim_arity(f) + def z(*paArgs): + thisFunc = f.__name__ + s,l,t = paArgs[-3:] + if len(paArgs)>3: + thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc + sys.stderr.write( ">>entering %s(line: '%s', %d, %r)\n" % (thisFunc,line(l,s),l,t) ) + try: + ret = f(*paArgs) + except Exception as exc: + sys.stderr.write( "<<leaving %s (exception: %s)\n" % (thisFunc,exc) ) + raise + sys.stderr.write( "<<leaving %s (ret: %r)\n" % (thisFunc,ret) ) + return ret + try: + z.__name__ = f.__name__ + except AttributeError: + pass + return z + +# +# global helpers +# +def delimitedList( expr, delim=",", combine=False ): + """ + Helper to define a delimited list of expressions - the delimiter defaults to ','. + By default, the list elements and delimiters can have intervening whitespace, and + comments, but this can be overridden by passing C{combine=True} in the constructor. + If C{combine} is set to C{True}, the matching tokens are returned as a single token + string, with the delimiters included; otherwise, the matching tokens are returned + as a list of tokens, with the delimiters suppressed. + + Example:: + delimitedList(Word(alphas)).parseString("aa,bb,cc") # -> ['aa', 'bb', 'cc'] + delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE'] + """ + dlName = _ustr(expr)+" ["+_ustr(delim)+" "+_ustr(expr)+"]..." + if combine: + return Combine( expr + ZeroOrMore( delim + expr ) ).setName(dlName) + else: + return ( expr + ZeroOrMore( Suppress( delim ) + expr ) ).setName(dlName) + +def countedArray( expr, intExpr=None ): + """ + Helper to define a counted list of expressions. + This helper defines a pattern of the form:: + integer expr expr expr... + where the leading integer tells how many expr expressions follow. + The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed. + + If C{intExpr} is specified, it should be a pyparsing expression that produces an integer value. + + Example:: + countedArray(Word(alphas)).parseString('2 ab cd ef') # -> ['ab', 'cd'] + + # in this parser, the leading integer value is given in binary, + # '10' indicating that 2 values are in the array + binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2)) + countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef') # -> ['ab', 'cd'] + """ + arrayExpr = Forward() + def countFieldParseAction(s,l,t): + n = t[0] + arrayExpr << (n and Group(And([expr]*n)) or Group(empty)) + return [] + if intExpr is None: + intExpr = Word(nums).setParseAction(lambda t:int(t[0])) + else: + intExpr = intExpr.copy() + intExpr.setName("arrayLen") + intExpr.addParseAction(countFieldParseAction, callDuringTry=True) + return ( intExpr + arrayExpr ).setName('(len) ' + _ustr(expr) + '...') + +def _flatten(L): + ret = [] + for i in L: + if isinstance(i,list): + ret.extend(_flatten(i)) + else: + ret.append(i) + return ret + +def matchPreviousLiteral(expr): + """ + Helper to define an expression that is indirectly defined from + the tokens matched in a previous expression, that is, it looks + for a 'repeat' of a previous expression. For example:: + first = Word(nums) + second = matchPreviousLiteral(first) + matchExpr = first + ":" + second + will match C{"1:1"}, but not C{"1:2"}. Because this matches a + previous literal, will also match the leading C{"1:1"} in C{"1:10"}. + If this is not desired, use C{matchPreviousExpr}. + Do I{not} use with packrat parsing enabled. + """ + rep = Forward() + def copyTokenToRepeater(s,l,t): + if t: + if len(t) == 1: + rep << t[0] + else: + # flatten t tokens + tflat = _flatten(t.asList()) + rep << And(Literal(tt) for tt in tflat) + else: + rep << Empty() + expr.addParseAction(copyTokenToRepeater, callDuringTry=True) + rep.setName('(prev) ' + _ustr(expr)) + return rep + +def matchPreviousExpr(expr): + """ + Helper to define an expression that is indirectly defined from + the tokens matched in a previous expression, that is, it looks + for a 'repeat' of a previous expression. For example:: + first = Word(nums) + second = matchPreviousExpr(first) + matchExpr = first + ":" + second + will match C{"1:1"}, but not C{"1:2"}. Because this matches by + expressions, will I{not} match the leading C{"1:1"} in C{"1:10"}; + the expressions are evaluated first, and then compared, so + C{"1"} is compared with C{"10"}. + Do I{not} use with packrat parsing enabled. + """ + rep = Forward() + e2 = expr.copy() + rep <<= e2 + def copyTokenToRepeater(s,l,t): + matchTokens = _flatten(t.asList()) + def mustMatchTheseTokens(s,l,t): + theseTokens = _flatten(t.asList()) + if theseTokens != matchTokens: + raise ParseException("",0,"") + rep.setParseAction( mustMatchTheseTokens, callDuringTry=True ) + expr.addParseAction(copyTokenToRepeater, callDuringTry=True) + rep.setName('(prev) ' + _ustr(expr)) + return rep + +def _escapeRegexRangeChars(s): + #~ escape these chars: ^-] + for c in r"\^-]": + s = s.replace(c,_bslash+c) + s = s.replace("\n",r"\n") + s = s.replace("\t",r"\t") + return _ustr(s) + +def oneOf( strs, caseless=False, useRegex=True ): + """ + Helper to quickly define a set of alternative Literals, and makes sure to do + longest-first testing when there is a conflict, regardless of the input order, + but returns a C{L{MatchFirst}} for best performance. + + Parameters: + - strs - a string of space-delimited literals, or a collection of string literals + - caseless - (default=C{False}) - treat all literals as caseless + - useRegex - (default=C{True}) - as an optimization, will generate a Regex + object; otherwise, will generate a C{MatchFirst} object (if C{caseless=True}, or + if creating a C{Regex} raises an exception) + + Example:: + comp_oper = oneOf("< = > <= >= !=") + var = Word(alphas) + number = Word(nums) + term = var | number + comparison_expr = term + comp_oper + term + print(comparison_expr.searchString("B = 12 AA=23 B<=AA AA>12")) + prints:: + [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']] + """ + if caseless: + isequal = ( lambda a,b: a.upper() == b.upper() ) + masks = ( lambda a,b: b.upper().startswith(a.upper()) ) + parseElementClass = CaselessLiteral + else: + isequal = ( lambda a,b: a == b ) + masks = ( lambda a,b: b.startswith(a) ) + parseElementClass = Literal + + symbols = [] + if isinstance(strs,basestring): + symbols = strs.split() + elif isinstance(strs, Iterable): + symbols = list(strs) + else: + warnings.warn("Invalid argument to oneOf, expected string or iterable", + SyntaxWarning, stacklevel=2) + if not symbols: + return NoMatch() + + i = 0 + while i < len(symbols)-1: + cur = symbols[i] + for j,other in enumerate(symbols[i+1:]): + if ( isequal(other, cur) ): + del symbols[i+j+1] + break + elif ( masks(cur, other) ): + del symbols[i+j+1] + symbols.insert(i,other) + cur = other + break + else: + i += 1 + + if not caseless and useRegex: + #~ print (strs,"->", "|".join( [ _escapeRegexChars(sym) for sym in symbols] )) + try: + if len(symbols)==len("".join(symbols)): + return Regex( "[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols) ).setName(' | '.join(symbols)) + else: + return Regex( "|".join(re.escape(sym) for sym in symbols) ).setName(' | '.join(symbols)) + except Exception: + warnings.warn("Exception creating Regex for oneOf, building MatchFirst", + SyntaxWarning, stacklevel=2) + + + # last resort, just use MatchFirst + return MatchFirst(parseElementClass(sym) for sym in symbols).setName(' | '.join(symbols)) + +def dictOf( key, value ): + """ + Helper to easily and clearly define a dictionary by specifying the respective patterns + for the key and value. Takes care of defining the C{L{Dict}}, C{L{ZeroOrMore}}, and C{L{Group}} tokens + in the proper order. The key pattern can include delimiting markers or punctuation, + as long as they are suppressed, thereby leaving the significant key text. The value + pattern can include named results, so that the C{Dict} results can include named token + fields. + + Example:: + text = "shape: SQUARE posn: upper left color: light blue texture: burlap" + attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + print(OneOrMore(attr_expr).parseString(text).dump()) + + attr_label = label + attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join) + + # similar to Dict, but simpler call format + result = dictOf(attr_label, attr_value).parseString(text) + print(result.dump()) + print(result['shape']) + print(result.shape) # object attribute access works too + print(result.asDict()) + prints:: + [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] + - color: light blue + - posn: upper left + - shape: SQUARE + - texture: burlap + SQUARE + SQUARE + {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'} + """ + return Dict( ZeroOrMore( Group ( key + value ) ) ) + +def originalTextFor(expr, asString=True): + """ + Helper to return the original, untokenized text for a given expression. Useful to + restore the parsed fields of an HTML start tag into the raw tag text itself, or to + revert separate tokens with intervening whitespace back to the original matching + input text. By default, returns astring containing the original parsed text. + + If the optional C{asString} argument is passed as C{False}, then the return value is a + C{L{ParseResults}} containing any results names that were originally matched, and a + single token containing the original matched text from the input string. So if + the expression passed to C{L{originalTextFor}} contains expressions with defined + results names, you must set C{asString} to C{False} if you want to preserve those + results name values. + + Example:: + src = "this is test <b> bold <i>text</i> </b> normal text " + for tag in ("b","i"): + opener,closer = makeHTMLTags(tag) + patt = originalTextFor(opener + SkipTo(closer) + closer) + print(patt.searchString(src)[0]) + prints:: + ['<b> bold <i>text</i> </b>'] + ['<i>text</i>'] + """ + locMarker = Empty().setParseAction(lambda s,loc,t: loc) + endlocMarker = locMarker.copy() + endlocMarker.callPreparse = False + matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end") + if asString: + extractText = lambda s,l,t: s[t._original_start:t._original_end] + else: + def extractText(s,l,t): + t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]] + matchExpr.setParseAction(extractText) + matchExpr.ignoreExprs = expr.ignoreExprs + return matchExpr + +def ungroup(expr): + """ + Helper to undo pyparsing's default grouping of And expressions, even + if all but one are non-empty. + """ + return TokenConverter(expr).setParseAction(lambda t:t[0]) + +def locatedExpr(expr): + """ + Helper to decorate a returned token with its starting and ending locations in the input string. + This helper adds the following results names: + - locn_start = location where matched expression begins + - locn_end = location where matched expression ends + - value = the actual parsed results + + Be careful if the input text contains C{<TAB>} characters, you may want to call + C{L{ParserElement.parseWithTabs}} + + Example:: + wd = Word(alphas) + for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"): + print(match) + prints:: + [[0, 'ljsdf', 5]] + [[8, 'lksdjjf', 15]] + [[18, 'lkkjj', 23]] + """ + locator = Empty().setParseAction(lambda s,l,t: l) + return Group(locator("locn_start") + expr("value") + locator.copy().leaveWhitespace()("locn_end")) + + +# convenience constants for positional expressions +empty = Empty().setName("empty") +lineStart = LineStart().setName("lineStart") +lineEnd = LineEnd().setName("lineEnd") +stringStart = StringStart().setName("stringStart") +stringEnd = StringEnd().setName("stringEnd") + +_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1]) +_escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s,l,t:unichr(int(t[0].lstrip(r'\0x'),16))) +_escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s,l,t:unichr(int(t[0][1:],8))) +_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | CharsNotIn(r'\]', exact=1) +_charRange = Group(_singleChar + Suppress("-") + _singleChar) +_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]" + +def srange(s): + r""" + Helper to easily define string ranges for use in Word construction. Borrows + syntax from regexp '[]' string range definitions:: + srange("[0-9]") -> "0123456789" + srange("[a-z]") -> "abcdefghijklmnopqrstuvwxyz" + srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_" + The input string must be enclosed in []'s, and the returned string is the expanded + character set joined into a single string. + The values enclosed in the []'s may be: + - a single character + - an escaped character with a leading backslash (such as C{\-} or C{\]}) + - an escaped hex character with a leading C{'\x'} (C{\x21}, which is a C{'!'} character) + (C{\0x##} is also supported for backwards compatibility) + - an escaped octal character with a leading C{'\0'} (C{\041}, which is a C{'!'} character) + - a range of any of the above, separated by a dash (C{'a-z'}, etc.) + - any combination of the above (C{'aeiouy'}, C{'a-zA-Z0-9_$'}, etc.) + """ + _expanded = lambda p: p if not isinstance(p,ParseResults) else ''.join(unichr(c) for c in range(ord(p[0]),ord(p[1])+1)) + try: + return "".join(_expanded(part) for part in _reBracketExpr.parseString(s).body) + except Exception: + return "" + +def matchOnlyAtCol(n): + """ + Helper method for defining parse actions that require matching at a specific + column in the input text. + """ + def verifyCol(strg,locn,toks): + if col(locn,strg) != n: + raise ParseException(strg,locn,"matched token not at column %d" % n) + return verifyCol + +def replaceWith(replStr): + """ + Helper method for common parse actions that simply return a literal value. Especially + useful when used with C{L{transformString<ParserElement.transformString>}()}. + + Example:: + num = Word(nums).setParseAction(lambda toks: int(toks[0])) + na = oneOf("N/A NA").setParseAction(replaceWith(math.nan)) + term = na | num + + OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234] + """ + return lambda s,l,t: [replStr] + +def removeQuotes(s,l,t): + """ + Helper parse action for removing quotation marks from parsed quoted strings. + + Example:: + # by default, quotation marks are included in parsed results + quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"] + + # use removeQuotes to strip quotation marks from parsed results + quotedString.setParseAction(removeQuotes) + quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"] + """ + return t[0][1:-1] + +def tokenMap(func, *args): + """ + Helper to define a parse action by mapping a function to all elements of a ParseResults list.If any additional + args are passed, they are forwarded to the given function as additional arguments after + the token, as in C{hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))}, which will convert the + parsed data to an integer using base 16. + + Example (compare the last to example in L{ParserElement.transformString}:: + hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16)) + hex_ints.runTests(''' + 00 11 22 aa FF 0a 0d 1a + ''') + + upperword = Word(alphas).setParseAction(tokenMap(str.upper)) + OneOrMore(upperword).runTests(''' + my kingdom for a horse + ''') + + wd = Word(alphas).setParseAction(tokenMap(str.title)) + OneOrMore(wd).setParseAction(' '.join).runTests(''' + now is the winter of our discontent made glorious summer by this sun of york + ''') + prints:: + 00 11 22 aa FF 0a 0d 1a + [0, 17, 34, 170, 255, 10, 13, 26] + + my kingdom for a horse + ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE'] + + now is the winter of our discontent made glorious summer by this sun of york + ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York'] + """ + def pa(s,l,t): + return [func(tokn, *args) for tokn in t] + + try: + func_name = getattr(func, '__name__', + getattr(func, '__class__').__name__) + except Exception: + func_name = str(func) + pa.__name__ = func_name + + return pa + +upcaseTokens = tokenMap(lambda t: _ustr(t).upper()) +"""(Deprecated) Helper parse action to convert tokens to upper case. Deprecated in favor of L{pyparsing_common.upcaseTokens}""" + +downcaseTokens = tokenMap(lambda t: _ustr(t).lower()) +"""(Deprecated) Helper parse action to convert tokens to lower case. Deprecated in favor of L{pyparsing_common.downcaseTokens}""" + +def _makeTags(tagStr, xml): + """Internal helper to construct opening and closing tag expressions, given a tag name""" + if isinstance(tagStr,basestring): + resname = tagStr + tagStr = Keyword(tagStr, caseless=not xml) + else: + resname = tagStr.name + + tagAttrName = Word(alphas,alphanums+"_-:") + if (xml): + tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes ) + openTag = Suppress("<") + tagStr("tag") + \ + Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \ + Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") + else: + printablesLessRAbrack = "".join(c for c in printables if c not in ">") + tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack) + openTag = Suppress("<") + tagStr("tag") + \ + Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \ + Optional( Suppress("=") + tagAttrValue ) ))) + \ + Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") + closeTag = Combine(_L("</") + tagStr + ">") + + openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % resname) + closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("</%s>" % resname) + openTag.tag = resname + closeTag.tag = resname + return openTag, closeTag + +def makeHTMLTags(tagStr): + """ + Helper to construct opening and closing tag expressions for HTML, given a tag name. Matches + tags in either upper or lower case, attributes with namespaces and with quoted or unquoted values. + + Example:: + text = '<td>More info at the <a href="http://pyparsing.wikispaces.com">pyparsing</a> wiki page</td>' + # makeHTMLTags returns pyparsing expressions for the opening and closing tags as a 2-tuple + a,a_end = makeHTMLTags("A") + link_expr = a + SkipTo(a_end)("link_text") + a_end + + for link in link_expr.searchString(text): + # attributes in the <A> tag (like "href" shown here) are also accessible as named results + print(link.link_text, '->', link.href) + prints:: + pyparsing -> http://pyparsing.wikispaces.com + """ + return _makeTags( tagStr, False ) + +def makeXMLTags(tagStr): + """ + Helper to construct opening and closing tag expressions for XML, given a tag name. Matches + tags only in the given upper/lower case. + + Example: similar to L{makeHTMLTags} + """ + return _makeTags( tagStr, True ) + +def withAttribute(*args,**attrDict): + """ + Helper to create a validating parse action to be used with start tags created + with C{L{makeXMLTags}} or C{L{makeHTMLTags}}. Use C{withAttribute} to qualify a starting tag + with a required attribute value, to avoid false matches on common tags such as + C{<TD>} or C{<DIV>}. + + Call C{withAttribute} with a series of attribute names and values. Specify the list + of filter attributes names and values as: + - keyword arguments, as in C{(align="right")}, or + - as an explicit dict with C{**} operator, when an attribute name is also a Python + reserved word, as in C{**{"class":"Customer", "align":"right"}} + - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) + For attribute names with a namespace prefix, you must use the second form. Attribute + names are matched insensitive to upper/lower case. + + If just testing for C{class} (with or without a namespace), use C{L{withClass}}. + + To verify that the attribute exists, but without specifying a value, pass + C{withAttribute.ANY_VALUE} as the value. + + Example:: + html = ''' + <div> + Some text + <div type="grid">1 4 0 1 0</div> + <div type="graph">1,3 2,3 1,1</div> + <div>this has no type</div> + </div> + + ''' + div,div_end = makeHTMLTags("div") + + # only match div tag having a type attribute with value "grid" + div_grid = div().setParseAction(withAttribute(type="grid")) + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + # construct a match with any div tag having a type attribute, regardless of the value + div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + if args: + attrs = args[:] + else: + attrs = attrDict.items() + attrs = [(k,v) for k,v in attrs] + def pa(s,l,tokens): + for attrName,attrValue in attrs: + if attrName not in tokens: + raise ParseException(s,l,"no matching attribute " + attrName) + if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: + raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % + (attrName, tokens[attrName], attrValue)) + return pa +withAttribute.ANY_VALUE = object() + +def withClass(classname, namespace=''): + """ + Simplified version of C{L{withAttribute}} when matching on a div class - made + difficult because C{class} is a reserved word in Python. + + Example:: + html = ''' + <div> + Some text + <div class="grid">1 4 0 1 0</div> + <div class="graph">1,3 2,3 1,1</div> + <div>this <div> has no class</div> + </div> + + ''' + div,div_end = makeHTMLTags("div") + div_grid = div().setParseAction(withClass("grid")) + + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + classattr = "%s:class" % namespace if namespace else "class" + return withAttribute(**{classattr : classname}) + +opAssoc = _Constants() +opAssoc.LEFT = object() +opAssoc.RIGHT = object() + +def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ): + """ + Helper method for constructing grammars of expressions made up of + operators working in a precedence hierarchy. Operators may be unary or + binary, left- or right-associative. Parse actions can also be attached + to operator expressions. The generated parser will also recognize the use + of parentheses to override operator precedences (see example below). + + Note: if you define a deep operator list, you may see performance issues + when using infixNotation. See L{ParserElement.enablePackrat} for a + mechanism to potentially improve your parser performance. + + Parameters: + - baseExpr - expression representing the most basic element for the nested + - opList - list of tuples, one for each operator precedence level in the + expression grammar; each tuple is of the form + (opExpr, numTerms, rightLeftAssoc, parseAction), where: + - opExpr is the pyparsing expression for the operator; + may also be a string, which will be converted to a Literal; + if numTerms is 3, opExpr is a tuple of two expressions, for the + two operators separating the 3 terms + - numTerms is the number of terms for this operator (must + be 1, 2, or 3) + - rightLeftAssoc is the indicator whether the operator is + right or left associative, using the pyparsing-defined + constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}. + - parseAction is the parse action to be associated with + expressions matching this operator expression (the + parse action tuple member may be omitted); if the parse action + is passed a tuple or list of functions, this is equivalent to + calling C{setParseAction(*fn)} (L{ParserElement.setParseAction}) + - lpar - expression for matching left-parentheses (default=C{Suppress('(')}) + - rpar - expression for matching right-parentheses (default=C{Suppress(')')}) + + Example:: + # simple example of four-function arithmetic with ints and variable names + integer = pyparsing_common.signed_integer + varname = pyparsing_common.identifier + + arith_expr = infixNotation(integer | varname, + [ + ('-', 1, opAssoc.RIGHT), + (oneOf('* /'), 2, opAssoc.LEFT), + (oneOf('+ -'), 2, opAssoc.LEFT), + ]) + + arith_expr.runTests(''' + 5+3*6 + (5+3)*6 + -2--11 + ''', fullDump=False) + prints:: + 5+3*6 + [[5, '+', [3, '*', 6]]] + + (5+3)*6 + [[[5, '+', 3], '*', 6]] + + -2--11 + [[['-', 2], '-', ['-', 11]]] + """ + ret = Forward() + lastExpr = baseExpr | ( lpar + ret + rpar ) + for i,operDef in enumerate(opList): + opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] + termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr + if arity == 3: + if opExpr is None or len(opExpr) != 2: + raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") + opExpr1, opExpr2 = opExpr + thisExpr = Forward().setName(termName) + if rightLeftAssoc == opAssoc.LEFT: + if arity == 1: + matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) + else: + matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ + Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + elif rightLeftAssoc == opAssoc.RIGHT: + if arity == 1: + # try to avoid LR with this extra test + if not isinstance(opExpr, Optional): + opExpr = Optional(opExpr) + matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) + else: + matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ + Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + else: + raise ValueError("operator must indicate right or left associativity") + if pa: + if isinstance(pa, (tuple, list)): + matchExpr.setParseAction(*pa) + else: + matchExpr.setParseAction(pa) + thisExpr <<= ( matchExpr.setName(termName) | lastExpr ) + lastExpr = thisExpr + ret <<= lastExpr + return ret + +operatorPrecedence = infixNotation +"""(Deprecated) Former name of C{L{infixNotation}}, will be dropped in a future release.""" + +dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"').setName("string enclosed in double quotes") +sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("string enclosed in single quotes") +quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"'| + Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("quotedString using single or double quotes") +unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") + +def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()): + """ + Helper method for defining nested lists enclosed in opening and closing + delimiters ("(" and ")" are the default). + + Parameters: + - opener - opening character for a nested list (default=C{"("}); can also be a pyparsing expression + - closer - closing character for a nested list (default=C{")"}); can also be a pyparsing expression + - content - expression for items within the nested lists (default=C{None}) + - ignoreExpr - expression for ignoring opening and closing delimiters (default=C{quotedString}) + + If an expression is not provided for the content argument, the nested + expression will capture all whitespace-delimited content between delimiters + as a list of separate values. + + Use the C{ignoreExpr} argument to define expressions that may contain + opening or closing characters that should not be treated as opening + or closing characters for nesting, such as quotedString or a comment + expression. Specify multiple expressions using an C{L{Or}} or C{L{MatchFirst}}. + The default is L{quotedString}, but if no expressions are to be ignored, + then pass C{None} for this argument. + + Example:: + data_type = oneOf("void int short long char float double") + decl_data_type = Combine(data_type + Optional(Word('*'))) + ident = Word(alphas+'_', alphanums+'_') + number = pyparsing_common.number + arg = Group(decl_data_type + ident) + LPAR,RPAR = map(Suppress, "()") + + code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment)) + + c_function = (decl_data_type("type") + + ident("name") + + LPAR + Optional(delimitedList(arg), [])("args") + RPAR + + code_body("body")) + c_function.ignore(cStyleComment) + + source_code = ''' + int is_odd(int x) { + return (x%2); + } + + int dec_to_hex(char hchar) { + if (hchar >= '0' && hchar <= '9') { + return (ord(hchar)-ord('0')); + } else { + return (10+ord(hchar)-ord('A')); + } + } + ''' + for func in c_function.searchString(source_code): + print("%(name)s (%(type)s) args: %(args)s" % func) + + prints:: + is_odd (int) args: [['int', 'x']] + dec_to_hex (int) args: [['char', 'hchar']] + """ + if opener == closer: + raise ValueError("opening and closing strings cannot be the same") + if content is None: + if isinstance(opener,basestring) and isinstance(closer,basestring): + if len(opener) == 1 and len(closer)==1: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (empty.copy()+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS + ).setParseAction(lambda t:t[0].strip())) + else: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + ~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + raise ValueError("opening and closing arguments must be strings if no content expression is given") + ret = Forward() + if ignoreExpr is not None: + ret <<= Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) + else: + ret <<= Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) + ret.setName('nested %s%s expression' % (opener,closer)) + return ret + +def indentedBlock(blockStatementExpr, indentStack, indent=True): + """ + Helper method for defining space-delimited indentation blocks, such as + those used to define block statements in Python source code. + + Parameters: + - blockStatementExpr - expression defining syntax of statement that + is repeated within the indented block + - indentStack - list created by caller to manage indentation stack + (multiple statementWithIndentedBlock expressions within a single grammar + should share a common indentStack) + - indent - boolean indicating whether block must be indented beyond the + the current level; set to False for block of left-most statements + (default=C{True}) + + A valid block must contain at least one C{blockStatement}. + + Example:: + data = ''' + def A(z): + A1 + B = 100 + G = A2 + A2 + A3 + B + def BB(a,b,c): + BB1 + def BBA(): + bba1 + bba2 + bba3 + C + D + def spam(x,y): + def eggs(z): + pass + ''' + + + indentStack = [1] + stmt = Forward() + + identifier = Word(alphas, alphanums) + funcDecl = ("def" + identifier + Group( "(" + Optional( delimitedList(identifier) ) + ")" ) + ":") + func_body = indentedBlock(stmt, indentStack) + funcDef = Group( funcDecl + func_body ) + + rvalue = Forward() + funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") + rvalue << (funcCall | identifier | Word(nums)) + assignment = Group(identifier + "=" + rvalue) + stmt << ( funcDef | assignment | identifier ) + + module_body = OneOrMore(stmt) + + parseTree = module_body.parseString(data) + parseTree.pprint() + prints:: + [['def', + 'A', + ['(', 'z', ')'], + ':', + [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], + 'B', + ['def', + 'BB', + ['(', 'a', 'b', 'c', ')'], + ':', + [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], + 'C', + 'D', + ['def', + 'spam', + ['(', 'x', 'y', ')'], + ':', + [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] + """ + def checkPeerIndent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if curCol != indentStack[-1]: + if curCol > indentStack[-1]: + raise ParseFatalException(s,l,"illegal nesting") + raise ParseException(s,l,"not a peer entry") + + def checkSubIndent(s,l,t): + curCol = col(l,s) + if curCol > indentStack[-1]: + indentStack.append( curCol ) + else: + raise ParseException(s,l,"not a subentry") + + def checkUnindent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): + raise ParseException(s,l,"not an unindent") + indentStack.pop() + + NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) + INDENT = (Empty() + Empty().setParseAction(checkSubIndent)).setName('INDENT') + PEER = Empty().setParseAction(checkPeerIndent).setName('') + UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') + if indent: + smExpr = Group( Optional(NL) + + #~ FollowedBy(blockStatementExpr) + + INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) + else: + smExpr = Group( Optional(NL) + + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) + blockStatementExpr.ignore(_bslash + LineEnd()) + return smExpr.setName('indented block') + +alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") +punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") + +anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag')) +_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\'')) +commonHTMLEntity = Regex('&(?P<entity>' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") +def replaceHTMLEntity(t): + """Helper parser action to replace common HTML entities with their special characters""" + return _htmlEntityMap.get(t.entity) + +# it's easy to get these comment structures wrong - they're very common, so may as well make them available +cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment") +"Comment of the form C{/* ... */}" + +htmlComment = Regex(r"<!--[\s\S]*?-->").setName("HTML comment") +"Comment of the form C{<!-- ... -->}" + +restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") +dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment") +"Comment of the form C{// ... (to end of line)}" + +cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/'| dblSlashComment).setName("C++ style comment") +"Comment of either form C{L{cStyleComment}} or C{L{dblSlashComment}}" + +javaStyleComment = cppStyleComment +"Same as C{L{cppStyleComment}}" + +pythonStyleComment = Regex(r"#.*").setName("Python style comment") +"Comment of the form C{# ... (to end of line)}" + +_commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + + Optional( Word(" \t") + + ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem") +commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList") +"""(Deprecated) Predefined expression of 1 or more printable words or quoted strings, separated by commas. + This expression is deprecated in favor of L{pyparsing_common.comma_separated_list}.""" + +# some other useful expressions - using lower-case class name since we are really using this as a namespace +class pyparsing_common: + """ + Here are some common low-level expressions that may be useful in jump-starting parser development: + - numeric forms (L{integers<integer>}, L{reals<real>}, L{scientific notation<sci_real>}) + - common L{programming identifiers<identifier>} + - network addresses (L{MAC<mac_address>}, L{IPv4<ipv4_address>}, L{IPv6<ipv6_address>}) + - ISO8601 L{dates<iso8601_date>} and L{datetime<iso8601_datetime>} + - L{UUID<uuid>} + - L{comma-separated list<comma_separated_list>} + Parse actions: + - C{L{convertToInteger}} + - C{L{convertToFloat}} + - C{L{convertToDate}} + - C{L{convertToDatetime}} + - C{L{stripHTMLTags}} + - C{L{upcaseTokens}} + - C{L{downcaseTokens}} + + Example:: + pyparsing_common.number.runTests(''' + # any int or real number, returned as the appropriate type + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.fnumber.runTests(''' + # any int or real number, returned as float + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.hex_integer.runTests(''' + # hex numbers + 100 + FF + ''') + + pyparsing_common.fraction.runTests(''' + # fractions + 1/2 + -3/4 + ''') + + pyparsing_common.mixed_integer.runTests(''' + # mixed fractions + 1 + 1/2 + -3/4 + 1-3/4 + ''') + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(''' + # uuid + 12345678-1234-5678-1234-567812345678 + ''') + prints:: + # any int or real number, returned as the appropriate type + 100 + [100] + + -100 + [-100] + + +100 + [100] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # any int or real number, returned as float + 100 + [100.0] + + -100 + [-100.0] + + +100 + [100.0] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # hex numbers + 100 + [256] + + FF + [255] + + # fractions + 1/2 + [0.5] + + -3/4 + [-0.75] + + # mixed fractions + 1 + [1] + + 1/2 + [0.5] + + -3/4 + [-0.75] + + 1-3/4 + [1.75] + + # uuid + 12345678-1234-5678-1234-567812345678 + [UUID('12345678-1234-5678-1234-567812345678')] + """ + + convertToInteger = tokenMap(int) + """ + Parse action for converting parsed integers to Python int + """ + + convertToFloat = tokenMap(float) + """ + Parse action for converting parsed numbers to Python float + """ + + integer = Word(nums).setName("integer").setParseAction(convertToInteger) + """expression that parses an unsigned integer, returns an int""" + + hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int,16)) + """expression that parses a hexadecimal integer, returns an int""" + + signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) + """expression that parses an integer with optional leading sign, returns an int""" + + fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction") + """fractional expression of an integer divided by an integer, returns a float""" + fraction.addParseAction(lambda t: t[0]/t[-1]) + + mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") + """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" + mixed_integer.addParseAction(sum) + + real = Regex(r'[+-]?\d+\.\d*').setName("real number").setParseAction(convertToFloat) + """expression that parses a floating point number and returns a float""" + + sci_real = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) + """expression that parses a floating point number with optional scientific notation and returns a float""" + + # streamlining this expression makes the docs nicer-looking + number = (sci_real | real | signed_integer).streamline() + """any numeric expression, returns the corresponding Python type""" + + fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) + """any int or real number, returned as float""" + + identifier = Word(alphas+'_', alphanums+'_').setName("identifier") + """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" + + ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address") + "IPv4 address (C{0.0.0.0 - 255.255.255.255})" + + _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer") + _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part)*7).setName("full IPv6 address") + _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part)*(0,6)) + "::" + Optional(_ipv6_part + (':' + _ipv6_part)*(0,6))).setName("short IPv6 address") + _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8) + _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address") + ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address") + "IPv6 address (long, short, or mixed form)" + + mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address") + "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" + + @staticmethod + def convertToDate(fmt="%Y-%m-%d"): + """ + Helper to create a parse action for converting parsed date string to Python datetime.date + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%d"}) + + Example:: + date_expr = pyparsing_common.iso8601_date.copy() + date_expr.setParseAction(pyparsing_common.convertToDate()) + print(date_expr.parseString("1999-12-31")) + prints:: + [datetime.date(1999, 12, 31)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt).date() + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + @staticmethod + def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"): + """ + Helper to create a parse action for converting parsed datetime string to Python datetime.datetime + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%dT%H:%M:%S.%f"}) + + Example:: + dt_expr = pyparsing_common.iso8601_datetime.copy() + dt_expr.setParseAction(pyparsing_common.convertToDatetime()) + print(dt_expr.parseString("1999-12-31T23:59:59.999")) + prints:: + [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt) + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + iso8601_date = Regex(r'(?P<year>\d{4})(?:-(?P<month>\d\d)(?:-(?P<day>\d\d))?)?').setName("ISO8601 date") + "ISO8601 date (C{yyyy-mm-dd})" + + iso8601_datetime = Regex(r'(?P<year>\d{4})-(?P<month>\d\d)-(?P<day>\d\d)[T ](?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d(\.\d*)?)?)?(?P<tz>Z|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime") + "ISO8601 datetime (C{yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)}) - trailing seconds, milliseconds, and timezone optional; accepts separating C{'T'} or C{' '}" + + uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID") + "UUID (C{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})" + + _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress() + @staticmethod + def stripHTMLTags(s, l, tokens): + """ + Parse action to remove HTML tags from web page HTML source + + Example:: + # strip HTML links from normal text + text = '<td>More info at the <a href="http://pyparsing.wikispaces.com">pyparsing</a> wiki page</td>' + td,td_end = makeHTMLTags("TD") + table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end + + print(table_text.parseString(text).body) # -> 'More info at the pyparsing wiki page' + """ + return pyparsing_common._html_stripper.transformString(tokens[0]) + + _commasepitem = Combine(OneOrMore(~Literal(",") + ~LineEnd() + Word(printables, excludeChars=',') + + Optional( White(" \t") ) ) ).streamline().setName("commaItem") + comma_separated_list = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("comma separated list") + """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" + + upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper())) + """Parse action to convert tokens to upper case.""" + + downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower())) + """Parse action to convert tokens to lower case.""" + + +if __name__ == "__main__": + + selectToken = CaselessLiteral("select") + fromToken = CaselessLiteral("from") + + ident = Word(alphas, alphanums + "_$") + + columnName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) + columnNameList = Group(delimitedList(columnName)).setName("columns") + columnSpec = ('*' | columnNameList) + + tableName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) + tableNameList = Group(delimitedList(tableName)).setName("tables") + + simpleSQL = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables") + + # demo runTests method, including embedded comments in test string + simpleSQL.runTests(""" + # '*' as column list and dotted table name + select * from SYS.XYZZY + + # caseless match on "SELECT", and casts back to "select" + SELECT * from XYZZY, ABC + + # list of column names, and mixed case SELECT keyword + Select AA,BB,CC from Sys.dual + + # multiple tables + Select A, B, C from Sys.dual, Table2 + + # invalid SELECT keyword - should fail + Xelect A, B, C from Sys.dual + + # incomplete command - should fail + Select + + # invalid column name - should fail + Select ^^^ frox Sys.dual + + """) + + pyparsing_common.number.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + # any int or real number, returned as float + pyparsing_common.fnumber.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + pyparsing_common.hex_integer.runTests(""" + 100 + FF + """) + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(""" + 12345678-1234-5678-1234-567812345678 + """) diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/six.py b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/six.py new file mode 100644 index 0000000..190c023 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources/_vendor/six.py @@ -0,0 +1,868 @@ +"""Utilities for writing code that runs on Python 2 and 3""" + +# Copyright (c) 2010-2015 Benjamin Peterson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson <benjamin@python.org>" +__version__ = "1.10.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + raise tp, value, tb +""") + + +if sys.version_info[:2] == (3, 2): + exec_("""def raise_from(value, from_value): + if from_value is None: + raise value + raise value from from_value +""") +elif sys.version_info[:2] > (3, 2): + exec_("""def raise_from(value, from_value): + raise value from from_value +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + return wrapper +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources/extern/__init__.py b/Display/.venv/lib/python3.7/site-packages/pkg_resources/extern/__init__.py new file mode 100644 index 0000000..c1eb9e9 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources/extern/__init__.py @@ -0,0 +1,73 @@ +import sys + + +class VendorImporter: + """ + A PEP 302 meta path importer for finding optionally-vendored + or otherwise naturally-installed packages from root_name. + """ + + def __init__(self, root_name, vendored_names=(), vendor_pkg=None): + self.root_name = root_name + self.vendored_names = set(vendored_names) + self.vendor_pkg = vendor_pkg or root_name.replace('extern', '_vendor') + + @property + def search_path(self): + """ + Search first the vendor package then as a natural package. + """ + yield self.vendor_pkg + '.' + yield '' + + def find_module(self, fullname, path=None): + """ + Return self when fullname starts with root_name and the + target module is one vendored through this importer. + """ + root, base, target = fullname.partition(self.root_name + '.') + if root: + return + if not any(map(target.startswith, self.vendored_names)): + return + return self + + def load_module(self, fullname): + """ + Iterate over the search path to locate and load fullname. + """ + root, base, target = fullname.partition(self.root_name + '.') + for prefix in self.search_path: + try: + extant = prefix + target + __import__(extant) + mod = sys.modules[extant] + sys.modules[fullname] = mod + # mysterious hack: + # Remove the reference to the extant package/module + # on later Python versions to cause relative imports + # in the vendor package to resolve the same modules + # as those going through this importer. + if prefix and sys.version_info > (3, 3): + del sys.modules[extant] + return mod + except ImportError: + pass + else: + raise ImportError( + "The '{target}' package is required; " + "normally this is bundled with this package so if you get " + "this warning, consult the packager of your " + "distribution.".format(**locals()) + ) + + def install(self): + """ + Install this importer into sys.meta_path if not already present. + """ + if self not in sys.meta_path: + sys.meta_path.append(self) + + +names = 'packaging', 'pyparsing', 'six', 'appdirs' +VendorImporter(__name__, names).install() diff --git a/Display/.venv/lib/python3.7/site-packages/pkg_resources/py31compat.py b/Display/.venv/lib/python3.7/site-packages/pkg_resources/py31compat.py new file mode 100644 index 0000000..a381c42 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pkg_resources/py31compat.py @@ -0,0 +1,23 @@ +import os +import errno +import sys + +from .extern import six + + +def _makedirs_31(path, exist_ok=False): + try: + os.makedirs(path) + except OSError as exc: + if not exist_ok or exc.errno != errno.EEXIST: + raise + + +# rely on compatibility behavior until mode considerations +# and exists_ok considerations are disentangled. +# See https://github.com/pypa/setuptools/pull/1083#issuecomment-315168663 +needs_makedirs = ( + six.PY2 or + (3, 4) <= sys.version_info < (3, 4, 1) +) +makedirs = _makedirs_31 if needs_makedirs else os.makedirs diff --git a/Display/.venv/lib/python3.7/site-packages/pygame-2.0.0.dev8.dist-info/INSTALLER b/Display/.venv/lib/python3.7/site-packages/pygame-2.0.0.dev8.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame-2.0.0.dev8.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/Display/.venv/lib/python3.7/site-packages/pygame-2.0.0.dev8.dist-info/METADATA b/Display/.venv/lib/python3.7/site-packages/pygame-2.0.0.dev8.dist-info/METADATA new file mode 100644 index 0000000..8f14e23 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame-2.0.0.dev8.dist-info/METADATA @@ -0,0 +1,15 @@ +Metadata-Version: 2.1 +Name: pygame +Version: 2.0.0.dev8 +Summary: Python Game Development +Home-page: https://www.pygame.org +Author: A community project. +Author-email: pygame@pygame.org +License: LGPL +Platform: UNKNOWN + +Pygame is a Python wrapper module for the +SDL multimedia library. It contains python functions and classes +that will allow you to use SDL's support for playing cdroms, +audio and video output, and keyboard, mouse and joystick input. + diff --git a/Display/.venv/lib/python3.7/site-packages/pygame-2.0.0.dev8.dist-info/RECORD b/Display/.venv/lib/python3.7/site-packages/pygame-2.0.0.dev8.dist-info/RECORD new file mode 100644 index 0000000..9b875d0 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame-2.0.0.dev8.dist-info/RECORD @@ -0,0 +1,544 @@ +../../../include/site/python3.7/pygame/_camera.h,sha256=4dX0hz1SNd_5xZn0kuwHw6-3pGxEOuj1XcENVbAeucg,840 +../../../include/site/python3.7/pygame/_pygame.h,sha256=S7NGvlsumrNvXF2ec7-_RPp1AGtaD9l_bdBi4B-FIgw,6470 +../../../include/site/python3.7/pygame/_surface.h,sha256=G6ICVNMqd3DqzBtTh1BxFxW1bs2fPrO__vcBUQtbwRM,958 +../../../include/site/python3.7/pygame/camera.h,sha256=P-8jQQqs13E-S_5VV9QnWgzXJxfX4qsK9cQepvB-4GY,7016 +../../../include/site/python3.7/pygame/fastevents.h,sha256=OKgCTiH8K4ud2orTBJXeF5w3Mo0_ImbgRW5f5UTnCqY,1643 +../../../include/site/python3.7/pygame/font.h,sha256=sBfoJ6CngZX7br18guZ5WwmtxDhLjNbDkW20Q3APXss,340 +../../../include/site/python3.7/pygame/freetype.h,sha256=KssA4jt2Xn_4OwaxcztGyJYNnhpDHT3vXxH_SGU64hQ,3618 +../../../include/site/python3.7/pygame/include/_pygame.h,sha256=4wtPP4LFJCgsTjOSGyxbvE4JGPLc7VcZnJ3Yz3jRoRY,17871 +../../../include/site/python3.7/pygame/include/bitmask.h,sha256=CaVl446D6ccqUJhBfP704BJV8-mkMDcV8JCusQzljw4,4859 +../../../include/site/python3.7/pygame/include/pgcompat.h,sha256=L0dik3muUEixildmJxf_vM4mDr4MbnM4ahQNv9Ee8PA,4005 +../../../include/site/python3.7/pygame/include/pgimport.h,sha256=fWawkDYLJWnUFP1XpKUK8uASngAgNxple8DjNwop3_8,2845 +../../../include/site/python3.7/pygame/include/pgplatform.h,sha256=5PUP6dapRNEwTkWgEyB0VmDV0qShGUAhfxRIg_xa6LQ,1177 +../../../include/site/python3.7/pygame/include/pygame.h,sha256=90tX-gvapUFv077c3Z22x8r17DB0bmMYfQ7aduxOKHc,1246 +../../../include/site/python3.7/pygame/include/pygame_bufferproxy.h,sha256=eZZqVmFoJn4EGhKQlWs0yzvXufl5r8jfQY2Ydtoz_Ro,1860 +../../../include/site/python3.7/pygame/include/pygame_font.h,sha256=uE3GkusyVKM9QYcg_gjm0j6rwCtep8Y7mA8Bhe2VZgg,1469 +../../../include/site/python3.7/pygame/include/pygame_freetype.h,sha256=IaDEcY3KyxUpqcyFVcHcFaPX_W3BvHShsZtKp2rOEOc,1349 +../../../include/site/python3.7/pygame/include/pygame_mask.h,sha256=LNWcc-ulZBBfCzceHEkzTPE_G0ZWthC1hBouTN8DBYA,1310 +../../../include/site/python3.7/pygame/include/pygame_mixer.h,sha256=m4yQMNImWyI6nplsdNBuB6DZSiS9F5_vQciKZ8aDlnI,2205 +../../../include/site/python3.7/pygame/mask.h,sha256=Y7OqzNUqQQHchUsSlvd-ja5d9IgAfSV2uFlaa8_5Lys,153 +../../../include/site/python3.7/pygame/mixer.h,sha256=d_DLjpJJSqZkSLnbVIjEE2xDnv3Wj3W_MOC0QiW-IFw,340 +../../../include/site/python3.7/pygame/palette.h,sha256=dzARYIsQdHAaV8ypCrQbYRWFisXYXABD4ToMlzYKojg,7057 +../../../include/site/python3.7/pygame/pgarrinter.h,sha256=alsw7p6X7ukOB1o3curyrjWOcGHgVCQgCvS1D9FtiRc,1060 +../../../include/site/python3.7/pygame/pgbufferproxy.h,sha256=tqMDkdkH40QoYJ3NtTjiknAnSMh0i1sfNMaow3npvKI,179 +../../../include/site/python3.7/pygame/pgcompat.h,sha256=N8fdW2sCy60EzAMN3DUROj6H9Zn6N0YrkfbSvKSD5SU,6693 +../../../include/site/python3.7/pygame/pgimport.h,sha256=iLJzKy6r0-WENM5bRV1xL58_kEwpFNsGIu3vPxYF15E,364 +../../../include/site/python3.7/pygame/pgopengl.h,sha256=AxeopnU7kMuA_Gx4VsK_H91dCD9mOsH_-MyW7QHQ9y4,466 +../../../include/site/python3.7/pygame/pgplatform.h,sha256=N375axCJYbXIu6iD6RqYoIJRzWa3t7GJFlWpXkucg6Y,950 +../../../include/site/python3.7/pygame/pygame.h,sha256=AQcZWIoAWGmN9fYBynCXxc81hd9lcwxBwWeq8WZjTKE,1083 +../../../include/site/python3.7/pygame/scrap.h,sha256=wPqvHsRiawISZKqdVaKglFge5gdegHz5pgDv9EDFXWI,4649 +../../../include/site/python3.7/pygame/surface.h,sha256=gbtI4NuqYrVty0iJLXgPwc_7tQjtRy1FJgHQ-bCeFuI,14554 +pygame-2.0.0.dev8.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pygame-2.0.0.dev8.dist-info/METADATA,sha256=Y8hTc6_dpPrGcAxaRiJL7v6P2J4lojhGiSOmpmXCjO0,449 +pygame-2.0.0.dev8.dist-info/RECORD,, +pygame-2.0.0.dev8.dist-info/WHEEL,sha256=AhV6RMqZ2IDfreRJKo44QWYxYeP-0Jr0bezzBLQ1eog,109 +pygame-2.0.0.dev8.dist-info/top_level.txt,sha256=ABXdFGIAE2g9m2VOzQPaLa917r6XEu6d96RqIzvAWCs,7 +pygame/.libs/libFLAC-bf6d1292.so.8.3.0,sha256=MbiBkVUySTuXHDYCAkjcP5Q_cD6NhJjrgnaO9Uq2rDE,1741296 +pygame/.libs/libSDL2-2-a810f3c1.0.so.0.12.0,sha256=kzRkLNDbXQPhbS7RYzU2EU98GFSx9qpN1Uk6y4IdBhg,8478464 +pygame/.libs/libSDL2_image-2-4bd2d50c.0.so.0.2.3,sha256=ij2Ae3JDUcHBzd3sKGgrEOtOiAxbAx9fZwZcJREw0Sg,674800 +pygame/.libs/libSDL2_mixer-2-ee256d43.0.so.0.2.2,sha256=e32Q41U95Ib-rRWSaxGJSf1k7dJlarEBYKWBkJUAGJ0,589200 +pygame/.libs/libSDL2_ttf-2-9c5a5e5f.0.so.0.14.1,sha256=sm6fiTn4FnVtdQYUYKl4ax_dWv7Vci6YafJRFYFjKK4,153552 +pygame/.libs/libasound-87b0bd31.so.2.0.0,sha256=-btnGo21ZVN9mUki0pTC-SZEZsplUkkzhuqCAcWCDRM,5163104 +pygame/.libs/libfreetype-2d39c124.so.6.17.1,sha256=SQlXrWfZ4jpzsGlB4eW1bVcB0KJHK9Kn_MvgCZE1Q2w,3621760 +pygame/.libs/libjpeg-bd53fca1.so.62.0.0,sha256=yZ0kGqDfPVsvI99n871cQ8mRpHDEqGPXifmOx7JhKek,142552 +pygame/.libs/libmikmod-fabcac29.so.2.0.4,sha256=uTWRQ175POhVc28RHG_MJmeQ5-Vmq9IA_50pK7CHCZw,308648 +pygame/.libs/libogg-b51fbe74.so.0.8.4,sha256=g2VXD-AmoZNejxbRLjuZdtm7jxuZJPakNsas0CMT7Mo,84064 +pygame/.libs/libpng16-b14e7f97.so.16.37.0,sha256=w4S8wicm12mmeIAIO9AnDfquvpwdeflyLhSwsaHPOa4,959776 +pygame/.libs/libportmidi-7fcf6c23.so,sha256=FovmpEeh0niUlMIH8Y_ZYAsevY2L0oDeWpHU5AHUD88,59984 +pygame/.libs/libtiff-97e44e95.so.3.8.2,sha256=3EAWsGH6ZDeUN-SiYcUAftypU0kU1kV0O-KlSCo8jVw,389776 +pygame/.libs/libvorbis-205f0f59.so.0.4.8,sha256=3yLKuCEBPp9SKe5Q8zWgOV2SpF18QcZoT6SB1QPT1TY,240344 +pygame/.libs/libvorbisfile-f207f3a6.so.3.3.7,sha256=Ajy053OHMOrkG4_bec1twG3_5iU8zwdLaE9mUYRb0UE,63128 +pygame/.libs/libwebp-582c46b3.so.7.1.0,sha256=S6BDR62e2NyrGDnAho9M1QoXBvj435WSJk4JvbOtkRc,3627752 +pygame/.libs/libz-a147dcb0.so.1.2.3,sha256=VwXH3AM7bnoa793tKDw_H0pW-VZos08-FEtM_g_VWVM,87848 +pygame/__init__.py,sha256=WyitdIySlwxok1qBfFavWHvonvY-tOWIImwLunNHmmo,10142 +pygame/__init__.pyi,sha256=i5lGaChNnMGAW5k5D_LHq-nzjJhmSDs_hLXfY9qgRFo,1931 +pygame/__pycache__/__init__.cpython-37.pyc,, +pygame/__pycache__/_camera_opencv_highgui.cpython-37.pyc,, +pygame/__pycache__/_camera_vidcapture.cpython-37.pyc,, +pygame/__pycache__/_dummybackend.cpython-37.pyc,, +pygame/__pycache__/_numpysndarray.cpython-37.pyc,, +pygame/__pycache__/_numpysurfarray.cpython-37.pyc,, +pygame/__pycache__/camera.cpython-37.pyc,, +pygame/__pycache__/colordict.cpython-37.pyc,, +pygame/__pycache__/compat.cpython-37.pyc,, +pygame/__pycache__/cursors.cpython-37.pyc,, +pygame/__pycache__/draw_py.cpython-37.pyc,, +pygame/__pycache__/freetype.cpython-37.pyc,, +pygame/__pycache__/ftfont.cpython-37.pyc,, +pygame/__pycache__/locals.cpython-37.pyc,, +pygame/__pycache__/macosx.cpython-37.pyc,, +pygame/__pycache__/midi.cpython-37.pyc,, +pygame/__pycache__/pkgdata.cpython-37.pyc,, +pygame/__pycache__/sndarray.cpython-37.pyc,, +pygame/__pycache__/sprite.cpython-37.pyc,, +pygame/__pycache__/surfarray.cpython-37.pyc,, +pygame/__pycache__/sysfont.cpython-37.pyc,, +pygame/__pycache__/version.cpython-37.pyc,, +pygame/_camera.cpython-37m-x86_64-linux-gnu.so,sha256=i0cpa5qneAz_3CfqNuYdAZ54yVFP1M03qFg-kOHHOi4,219408 +pygame/_camera_opencv_highgui.py,sha256=0BxVKRNVVKV6De4Fj7bjHY6xZgFPD7DG43X8sJ7YC9o,2230 +pygame/_camera_vidcapture.py,sha256=7ASlHA-ub1kAgIVRkOG-vkytcaA_aHTldbGYAQcOxdQ,3740 +pygame/_dummybackend.py,sha256=cIt88kBhPzgset-VkJ1D3bG10pvlXLevpqRgrHhbeuo,770 +pygame/_freetype.cpython-37m-x86_64-linux-gnu.so,sha256=Wcg3L-Z2e-1_83GXxl0sWcCRd8l2O6GfaXX2v4zUf1I,462888 +pygame/_numpysndarray.py,sha256=yi80QLq544_RS_Z2dJxxBgvNmxLimEmOMIyULAFCOl0,2601 +pygame/_numpysurfarray.py,sha256=VFpjc2__I02y7J0qnZ2Vno1E1DWN2vApkP8P0CWLVeg,12908 +pygame/_sdl2/__init__.py,sha256=-Xo7wRIPBZaSNpDyNV0x_I7qjVILcxrGhZPsV5nv0ew,62 +pygame/_sdl2/__init__.pyi,sha256=iY7Itz22r2Acd_9NNzp7VZwLX27uatZCq82jzgXodKM,21 +pygame/_sdl2/__pycache__/__init__.cpython-37.pyc,, +pygame/_sdl2/audio.cpython-37m-x86_64-linux-gnu.so,sha256=ysX4UIlJsWqgFY12m5DibBYLyWqDIaupRTpaWaasx5Q,1040472 +pygame/_sdl2/controller.cpython-37m-x86_64-linux-gnu.so,sha256=o1TKjk-Zs_lhftx3ZxIJ0EoTTi6Mksiqq-23OHlFPOw,408944 +pygame/_sdl2/mixer.cpython-37m-x86_64-linux-gnu.so,sha256=U2spKTxjDNpmLKRyhbkAs9l0bbrZXwj9WFWdQ_hhD_c,929352 +pygame/_sdl2/sdl2.cpython-37m-x86_64-linux-gnu.so,sha256=0ACmJfusffAAPvhGUJOcdVW23w__SpExuo1KndYKZ4U,193480 +pygame/_sdl2/touch.cpython-37m-x86_64-linux-gnu.so,sha256=yXFDIMUyeOTOv2MD2KxEyg24PkCe0PqH8qIS6iAWCHM,50552 +pygame/_sdl2/touch.pyi,sha256=-vYGJsSC18E2gitH10HIv_V_DUc6DIeCb55zuQp3jPc,231 +pygame/_sdl2/video.cpython-37m-x86_64-linux-gnu.so,sha256=3_RHPKUHvHSKo4DEn-kkfB1LN74yDjPLeMGC9S1Ecg8,1373648 +pygame/_sprite.cpython-37m-x86_64-linux-gnu.so,sha256=S97sOW7w737uPsP8pap42sFVkyFEP3Lai31_bgQ8RD8,1994560 +pygame/base.cpython-37m-x86_64-linux-gnu.so,sha256=nM7ehH2w_aiwklqV4lCvBJ7PHlUVVSjbXQdjebeu0O8,135472 +pygame/bufferproxy.cpython-37m-x86_64-linux-gnu.so,sha256=oSMr2m2Q5PF0iLq_6xVMRrvd0RciKkjlpiAZLsjQv_U,79448 +pygame/bufferproxy.pyi,sha256=pRadT45Kf9DWpYJGHrBgQgnisOrWGjS7V50WhRzeVTo,363 +pygame/camera.py,sha256=fSl40HdUPKvkRqf23EeO9zu0VXd4ZALcmhpfaTWvZNM,2876 +pygame/camera.pyi,sha256=Vm_McBIbvO3MnpbRg1MRDpqzXWIUA69FjvbBvGEsjmg,862 +pygame/color.cpython-37m-x86_64-linux-gnu.so,sha256=vxQOLkHvfAz4BPL56J5QnyN--d3du-0OR-iE9DbR74w,165784 +pygame/color.pyi,sha256=ZU96ZRQiBOE-J7Q7_UIzcfNkxuI_kurx3Xqbgn6E6go,737 +pygame/colordict.py,sha256=3Xu50TCCxRdi-ue8Vp9YBGQpmKOsv1rU9bOxf3kpkR4,24175 +pygame/compat.py,sha256=ybSN_4HbNWvDJ6quCTklD1hATz8LyN0bIeRzN2yTqD8,3113 +pygame/constants.cpython-37m-x86_64-linux-gnu.so,sha256=qTC8OgtdWBfyQLUaMpivyRhkPGs9AfrNgTM7avVXym8,185560 +pygame/constants.pyi,sha256=1qI5taiS0pX9fbEug2Au-uynSFps_OHNOJDT8V7BD0I,9064 +pygame/cursors.py,sha256=chI24h7Tnfv3TiDBNDyfn6ztzXZmZBJZjYppfcgjtgo,10047 +pygame/cursors.pyi,sha256=7EF4PlPtvPB5ICvh9a6IKV1syaRDopCRH2rZ8fJhJgs,1749 +pygame/display.cpython-37m-x86_64-linux-gnu.so,sha256=zq9Yv5xSQiejEz9H0PAxbD6PZA_Fdy8DfqE-5bk03BA,175240 +pygame/display.pyi,sha256=fqgcDROheHsawhk3tGXUVpCreZoOMUclaSSeaxnzJ0M,2417 +pygame/docs/__init__.py,sha256=29vwukb_6yJPWEe66QZQynt10HvgrYBmGP2vWm7Nt_w,287 +pygame/docs/__main__.py,sha256=5r8cR_y261Kf_QDTxmw9hCdFaVBGexXV5-OrQLbSNac,750 +pygame/docs/__pycache__/__init__.cpython-37.pyc,, +pygame/docs/__pycache__/__main__.cpython-37.pyc,, +pygame/docs/logos.html,sha256=Av4WDVwgX0xfhNJ-GJ2hFARHmZiDi_yv0WGk8FKzvJI,1604 +pygame/docs/pygame_logo.gif,sha256=XTZuyss-niaQhtGw78vP9MQmqIH_yLgq6cJqtb8hRtI,25116 +pygame/docs/pygame_powered.gif,sha256=JOcurZ9ApwPx420aiIOPFgoKFHkhpQKbll0w9-qhvnY,10171 +pygame/docs/pygame_small.gif,sha256=OT5k5n6OnoatNH9HfWETFx88Cr3PrUzkZyZritBviOM,10286 +pygame/docs/pygame_tiny.gif,sha256=vx7ERhvSpj51wp_qu-jISAgQr8E6vxK4_3I58-oSRm0,5485 +pygame/docs/ref/docscomments.json,sha256=tWP-5RPjrhOWON7xpzytgjYp8ZzSbeT6e89sy6IGCk8,476460 +pygame/draw.cpython-37m-x86_64-linux-gnu.so,sha256=DX8JgOxKX8FjuYMHNwnLpDgT4tUN1DYMEOW0vpGVIkY,189824 +pygame/draw.pyi,sha256=FigEcbMY0D5Iqn_aDhBXgP70MBsIWHXKr9GS2uf1isA,2222 +pygame/draw_py.py,sha256=gv5TNP74JkYuDbSl_-pCC8AffIOENe34cY28NO-UTgs,17984 +pygame/event.cpython-37m-x86_64-linux-gnu.so,sha256=Cq6IRjGgwRm-_8oS7OQiYk0E6M-K8JCbMaNAruku24o,194736 +pygame/event.pyi,sha256=2geKoOVzl3d6jKxL29IDD6AXN7ciV9OStajSbCUHFJ4,1044 +pygame/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pygame/examples/__pycache__/__init__.cpython-37.pyc,, +pygame/examples/__pycache__/aacircle.cpython-37.pyc,, +pygame/examples/__pycache__/aliens.cpython-37.pyc,, +pygame/examples/__pycache__/arraydemo.cpython-37.pyc,, +pygame/examples/__pycache__/audiocapture.cpython-37.pyc,, +pygame/examples/__pycache__/blend_fill.cpython-37.pyc,, +pygame/examples/__pycache__/blit_blends.cpython-37.pyc,, +pygame/examples/__pycache__/camera.cpython-37.pyc,, +pygame/examples/__pycache__/chimp.cpython-37.pyc,, +pygame/examples/__pycache__/cursors.cpython-37.pyc,, +pygame/examples/__pycache__/dropevent.cpython-37.pyc,, +pygame/examples/__pycache__/eventlist.cpython-37.pyc,, +pygame/examples/__pycache__/fastevents.cpython-37.pyc,, +pygame/examples/__pycache__/font_viewer.cpython-37.pyc,, +pygame/examples/__pycache__/fonty.cpython-37.pyc,, +pygame/examples/__pycache__/freetype_misc.cpython-37.pyc,, +pygame/examples/__pycache__/glcube.cpython-37.pyc,, +pygame/examples/__pycache__/headless_no_windows_needed.cpython-37.pyc,, +pygame/examples/__pycache__/liquid.cpython-37.pyc,, +pygame/examples/__pycache__/mask.cpython-37.pyc,, +pygame/examples/__pycache__/midi.cpython-37.pyc,, +pygame/examples/__pycache__/moveit.cpython-37.pyc,, +pygame/examples/__pycache__/music_drop_fade.cpython-37.pyc,, +pygame/examples/__pycache__/overlay.cpython-37.pyc,, +pygame/examples/__pycache__/pixelarray.cpython-37.pyc,, +pygame/examples/__pycache__/playmus.cpython-37.pyc,, +pygame/examples/__pycache__/prevent_display_stretching.cpython-37.pyc,, +pygame/examples/__pycache__/scaletest.cpython-37.pyc,, +pygame/examples/__pycache__/scrap_clipboard.cpython-37.pyc,, +pygame/examples/__pycache__/scroll.cpython-37.pyc,, +pygame/examples/__pycache__/setmodescale.cpython-37.pyc,, +pygame/examples/__pycache__/sound.cpython-37.pyc,, +pygame/examples/__pycache__/sound_array_demos.cpython-37.pyc,, +pygame/examples/__pycache__/sprite_texture.cpython-37.pyc,, +pygame/examples/__pycache__/stars.cpython-37.pyc,, +pygame/examples/__pycache__/testsprite.cpython-37.pyc,, +pygame/examples/__pycache__/textinput.cpython-37.pyc,, +pygame/examples/__pycache__/vgrade.cpython-37.pyc,, +pygame/examples/__pycache__/video.cpython-37.pyc,, +pygame/examples/aacircle.py,sha256=htt90P2ZAgyZiDtpbbN-hokBajJwCyE0IGP4Fi5FgVw,1062 +pygame/examples/aliens.py,sha256=Q8jI-b0CL-wWCYecUHpnmoZEB6OnMS8xeFg15nFHGNc,12127 +pygame/examples/arraydemo.py,sha256=faUrOfDd4DGaIZzQSrmQj08w2FyEGAmyLmlACUGUSqM,3616 +pygame/examples/audiocapture.py,sha256=81aWRR4tW-LxfONqDp09Wnd8ppMeDM1gHF8oB1TGww4,1718 +pygame/examples/blend_fill.py,sha256=LZ1u3axCmw7uunMFJkoyF2Wir74uIIOxDU1i2HFO-eg,3425 +pygame/examples/blit_blends.py,sha256=A89hJlNbAx8LiP4sosSmHr3fL2Xv6COF_riZrpENqGQ,6345 +pygame/examples/camera.py,sha256=u10VkjfAtEVHsQ9VWk9V7JIZmUXKOF1gqXlibyUVKpk,2987 +pygame/examples/chimp.py,sha256=dvg6ZvEtWt--2rohyO9ZeoSPKhjOkxg1vmSUOfUJxOk,5931 +pygame/examples/cursors.py,sha256=jsSrygbAfMZ4McUzJPxUCv5N1g7QcJAzRQE2y0fpAb0,2879 +pygame/examples/data/alien1.gif,sha256=8Wveo1zpLVaFCtYITm_SoYqjy8L-TDuaZOcNa8Osqsw,3826 +pygame/examples/data/alien1.jpg,sha256=HOjXjmW4Ofsu_en9WNrkuIp_DCwupXcFB0Yt_cqV9rA,3103 +pygame/examples/data/alien1.png,sha256=femzLssV7oGvT3S2tyviyq7qO32QfhBDtMOR3ENBCLs,3522 +pygame/examples/data/alien2.gif,sha256=0MPpVYzvjAECy0pd7YRFKCEzzIYDKEJt70rbjlLbTZM,3834 +pygame/examples/data/alien2.png,sha256=FKGYDI2FBBR1Z56BLn357PNfh3-M38gAJpSQL8BpKYY,3526 +pygame/examples/data/alien3.gif,sha256=bFCRGZOQPaadCKIc-tlqoUjHdsi5IzR0E-2SjpPEvmA,3829 +pygame/examples/data/alien3.png,sha256=a51Tb9E4IvoICGzQChHq51RKVQJLf1GOCEeqA5yYfnk,3518 +pygame/examples/data/arraydemo.bmp,sha256=xM4-n_hRCQFZlfwwdTK6eaBweycUc863TgSFbWp3dbA,76854 +pygame/examples/data/asprite.bmp,sha256=97XMpKq9lLpMuv8UveCf8UJEAxheBhPUjHfMRQBkUx4,578 +pygame/examples/data/background.gif,sha256=-3kZwt99MFUBbBo-kHvPZXVlFrSB34XVNQWWxfHb970,9133 +pygame/examples/data/blue.mpg,sha256=XDj1CRPt1MWxspCfA3oqb822nlZgQ7CyyEuVJwlgmpg,6144 +pygame/examples/data/bomb.gif,sha256=T4VCSOht8tpisgV5rIQnBCPs7vtSzAZBJF7SZ_L6JQM,1162 +pygame/examples/data/boom.wav,sha256=kfoWs0VVDGHv0JSa46nXZBGyw70-jpfPq_B31qNA_F8,12562 +pygame/examples/data/brick.png,sha256=K_mshK0aL81nzOjAorTXyPps6n9mvofLeOWFXFpVjYA,170 +pygame/examples/data/car_door.wav,sha256=TwYWVqme5NqVVID1N4es92RSKEdTYkxbNx6dNamK-_4,3910 +pygame/examples/data/chimp.bmp,sha256=PS9dLh1kfgnmba5lQiKyEQIBi8k-R1kvJ832SNudj1A,5498 +pygame/examples/data/city.png,sha256=c0Nu2o7x7QmvGMDmDCaPnhvJ8tPNuguKKpI_Z-NfQ40,143 +pygame/examples/data/danger.gif,sha256=m0CBKalFbkqlohgOmrwkwVOfqBhRWonb7xm1pzbDy2Q,2761 +pygame/examples/data/explosion1.gif,sha256=WYcdwbZqmYdaaaPYFiR5vka0Anp4F4nnNlpSSx_1xug,6513 +pygame/examples/data/fist.bmp,sha256=Nze8jhiCNl9wLgNYtVtRBqUGYqbn4-frAZaSVYXm_TE,4378 +pygame/examples/data/house_lo.mp3,sha256=R0nZUXymMp_XLPU8S1yvsiVeWT6MKLt5Rjp-WSnVrLQ,116320 +pygame/examples/data/house_lo.ogg,sha256=64FiQ1Zjq-cOj6Bmya_v3ZjEWmBaGZlTl19udKaz6sU,31334 +pygame/examples/data/house_lo.wav,sha256=B1BwfFaPIsSxaash-igVI_YE9SQd1BCXRTnSAKsNunY,78464 +pygame/examples/data/liquid.bmp,sha256=qtzPXhq0dr2ORNCCZ6gY2loT2Tsu0Dx5YvXB548I1Xg,11734 +pygame/examples/data/midikeys.png,sha256=9HCCmMHvlubR6G9a0jMv1C-AKeBzYfb5jjNhol2Mdqw,19666 +pygame/examples/data/oldplayer.gif,sha256=NWEhmaE5FUe0J-uCF8fr-XUAnoaqWa0SicoMQUBFYUg,1075 +pygame/examples/data/player1.gif,sha256=3ZTVWGxnedKqtf3R-X1omPC0Y8jUSPGgHBAzeGhnV4c,3470 +pygame/examples/data/punch.wav,sha256=A0F1xT8aIZ6aNI_5McMqLygb1EfmdIzPi4kWkU4EwQc,4176 +pygame/examples/data/sans.ttf,sha256=nrZ6FRet4dwlvA7xOReYCP2QwyGebk0iVJaSFbtpOhM,133088 +pygame/examples/data/secosmic_lo.wav,sha256=-EIFkzj7k5qEqG04n7mnUGUp1SsyCJ4n08TzPT600DY,18700 +pygame/examples/data/shot.gif,sha256=bF2eY629zQzvDu83AKpveSFhJq5G4QpOE98A0tvbPFI,129 +pygame/examples/data/static.png,sha256=Xe4wN80awt7nTNiLemoSNTEKlAbGFW7djNETP8IleNs,1202 +pygame/examples/data/whiff.wav,sha256=FMWM3XnYtce6mHFXQCYPgzT-xu-Q4DJybZfpPjG8cpE,5850 +pygame/examples/data/yuv_1.pgm,sha256=WGXoVZ0O-c6DTX9ALLoy-y4LFeOEul-W1PqFjBXGL20,649743 +pygame/examples/dropevent.py,sha256=BvidStsTzZoC4CURTc0muK6ErDd4Q3FJt6MDsNgMGcA,2240 +pygame/examples/eventlist.py,sha256=3WAEsQl4xgTvfixuM0Mu4nOx_0VzWiAX6Ipm1zNsvBc,5399 +pygame/examples/fastevents.py,sha256=jH95dsxJNwf5KzEs9tBYvTBhKasng_aN8rGkfzir-Wc,2410 +pygame/examples/font_viewer.py,sha256=YPXJfXjdDW57L6yx5XN9r1TCo5jif7aIayblwTB0BUY,9617 +pygame/examples/fonty.py,sha256=_hNDIQGmkewqOEH40n1TgfV6EeNGAyIp46-K-zpwV_Q,2437 +pygame/examples/freetype_misc.py,sha256=P--etI0yjMroYTD_g7ITx-3IIiiYGNuOAkpaUQaKZHA,4186 +pygame/examples/glcube.py,sha256=SjiTrLb_lNERE7H1rnx1tUceRB9IEWYVBFz16AB8_AM,3707 +pygame/examples/headless_no_windows_needed.py,sha256=DYSKwYk4EWjNtujuv2SAyQ_-8eKhVqiHGKdI67fBjd0,1301 +pygame/examples/liquid.py,sha256=zt7UNCxG9lZaRLl5C8sSMir-QV0x4oR48vttb2I6KJU,2527 +pygame/examples/mask.py,sha256=PN0bAy1AGPUO93Bluwy0bMeuRONnMV7-Gn795MltqWw,5867 +pygame/examples/midi.py,sha256=Hj4Z4mCNvtF83EglUXKQ0kZdtvKzCLPNwQiWpWfDtBw,29421 +pygame/examples/moveit.py,sha256=ChiN-U927z9ZOxK6ad7A5wXF9sc2bt2NrksMlxMMAHo,1810 +pygame/examples/music_drop_fade.py,sha256=Dbe-Z8PRfJbpUCcVyUhVHptN4mnfG3AU9-wZceZYVuE,9006 +pygame/examples/overlay.py,sha256=v3y65pwx6Au-AZmADx2enkNqPKBqHXGkVx78KHTzUEc,1479 +pygame/examples/pixelarray.py,sha256=m5fhZDONCLUTlYV0VwqqG4KUPGYJd4YfguHxoSfMNGg,3450 +pygame/examples/playmus.py,sha256=O8cK6Hkb7vJS0WHB3nYjEBDJbNhsU5LpQoY860DSEJM,4544 +pygame/examples/prevent_display_stretching.py,sha256=7pHLnGfM4rKB9CeJjjisKSynJzmTUbbBLbnCt7k4Iac,2478 +pygame/examples/scaletest.py,sha256=E4LsZWBxXDHU_diQLgTlm0X3v-VI_QbhXaE87nD9DGE,4852 +pygame/examples/scrap_clipboard.py,sha256=ssnb1k20YK48mEB3c0XM0450C3N2bgMny2LrD9uwjzQ,3081 +pygame/examples/scroll.py,sha256=c7kwmmVj_AX_3u5jce33d_L7mCrEzLBUHV9oNlA6bqs,6571 +pygame/examples/setmodescale.py,sha256=q5bYf6G08TZdJVmhPHrduMzQq3BInrqcN9nI8-0amXE,1772 +pygame/examples/sound.py,sha256=FuCTXOcGP0d1m7yGk2_qGsKPkshvBkK-prAr-polaZo,1158 +pygame/examples/sound_array_demos.py,sha256=R4AjxMUITzgrlzIqtBflgV6sHKe-ZQbImyjXcg6c5yY,5814 +pygame/examples/sprite_texture.py,sha256=E_WMS1ZZsFU4D4gArEZ4ecTPEToZuq5fnEn9TqI8hHA,2505 +pygame/examples/stars.py,sha256=IH4zE7XbqG3CT-ifekKC3WBXoIkrN0z_YRvYFH2UW6c,2748 +pygame/examples/testsprite.py,sha256=o3kVX012UWggL-tqtyj7z4cE6G0vBXGMIJNzjE35xDY,6944 +pygame/examples/textinput.py,sha256=hJQ6F3gnDi6kK_DUaZBym8-gTzgeiicUC5iYtpJERyc,5450 +pygame/examples/vgrade.py,sha256=eHdK5Ibq0i2tnj7qTOCzRwRigIONhor8cJfsgt_KJVw,3262 +pygame/examples/video.py,sha256=CkAJ4uRNdtqldRy7lYcRlYEdP--OcNOgX74-32eaBt8,4497 +pygame/fastevent.cpython-37m-x86_64-linux-gnu.so,sha256=B-fabS1B7IbZwDLrrsdIho7OT3QJ9DNNPMvE_A2mZxQ,79456 +pygame/fastevent.pyi,sha256=GRsdCsngJcF4Af8paysCocY2W8hLk8ygAc9-7IY1fvM,268 +pygame/font.cpython-37m-x86_64-linux-gnu.so,sha256=-FitCCWzcXD4c6XZ750gtkN2-FnaUMD5hNyQIvk1tzk,104568 +pygame/font.pyi,sha256=pFmDaDcQCbxPc6URv1uxcIt-u7iy6RkzK0K46Yu49c4,1399 +pygame/freesansbold.ttf,sha256=v5JRJp8R5LNVgqmTdglt7uPQxJc6RZy9l7C-vAH0QK0,98600 +pygame/freetype.py,sha256=EYAS2N0MjOJWsbf4Q67zmDBUb0wTKq89PG_cOArNGjk,2126 +pygame/freetype.pyi,sha256=GZh1l05EoksWZhajVXnSspIeKJXDGxpNUymcaqjYWRI,3500 +pygame/ftfont.py,sha256=3d1-f-X0AMIldsic3PRV0p5aeVcIQ2dzN5gqU_wFxRY,6200 +pygame/gfxdraw.cpython-37m-x86_64-linux-gnu.so,sha256=444-4ouKH-GeyqPRlifCFpzpF4A_2abJntCDgFznd80,321752 +pygame/gfxdraw.pyi,sha256=ruOxDbH2b_n-_2_oPwEBR-q4qcOPsVRqkzuWAVNfa9U,2798 +pygame/image.cpython-37m-x86_64-linux-gnu.so,sha256=eV05DP4rW9YDHcdwM8Dbq7cQDcF9BY4XwNWp55CdaPc,112416 +pygame/image.pyi,sha256=RIJEq_7fx1ZrqPu3rr4oAr5ZFXcikgAELLaKXAPK6tk,585 +pygame/imageext.cpython-37m-x86_64-linux-gnu.so,sha256=I1vHZOKCilEaN9x1r2BdWvmEIjjV03T10jM3MLVB-WA,108760 +pygame/joystick.cpython-37m-x86_64-linux-gnu.so,sha256=Bh9M9x4x5_ad0-gn9CqNIXCLDmwaRSeclC0NBmk4AAg,71296 +pygame/joystick.pyi,sha256=Gu0q39i9thtChNJyD3pqwHQODKPVZiataa8VQwo-eCI,766 +pygame/key.cpython-37m-x86_64-linux-gnu.so,sha256=CuIQUvDc-QBiCZMuRKLTSEdfsg5DlweU6ae2MyX90us,83488 +pygame/key.pyi,sha256=DnYhqg0PIUoeZ_xj73_4vqDe12AzcNCQd6zz2CSHOxs,757 +pygame/locals.py,sha256=8ZvWyqOqFiaPT5y7ru0_PHeCp15u_ecMqlRqNaNJ_As,1102 +pygame/macosx.py,sha256=VTPVdqrkkkLy8OmyVlgivDv-cpEEKGOj4XyLBlQBiW0,785 +pygame/mask.cpython-37m-x86_64-linux-gnu.so,sha256=0mnJwNsKrWcPqJpCd9DKzHaYNEMUnrFfzxb6SB7iO4g,223184 +pygame/mask.pyi,sha256=NH5eMCCSYBV5PVrKn-2gOATY1kI-T-SQX0WIgx6hLJ4,2885 +pygame/math.cpython-37m-x86_64-linux-gnu.so,sha256=WhPKwV77IkkYJpkO8K5Oh0cxkzGaitSoy5jpdCpymLo,302744 +pygame/math.pyi,sha256=UNZPHFCJ6jrx4JIioyla1u1HGKVo2aEInYvv1OU0BAw,4378 +pygame/midi.py,sha256=DzvxTm9h5Tnee2aE-3Ep9c-XGmrXIs6gq1axaSNsvDU,24046 +pygame/midi.pyi,sha256=j6KqkqpbNjFS_Kp65h8AKv2dDzPhlHr6qD49Ws8M5-s,2001 +pygame/mixer.cpython-37m-x86_64-linux-gnu.so,sha256=-HH1FHMgtVkSaPhEM6Vlzh-NOciM_sWPltdF1sybHdo,162312 +pygame/mixer.pyi,sha256=mxmV-dCb3hGJE2BfxpoHtmK87PSUASFkOI1E7VL1Tik,2349 +pygame/mixer_music.cpython-37m-x86_64-linux-gnu.so,sha256=N-WAq-SdfBaRdGBhMEMFstEhtsT93rno7Jsg8EpKECE,83664 +pygame/mouse.cpython-37m-x86_64-linux-gnu.so,sha256=Ff_XQ-8kOFu21K9-CqYOlm4wtOY8h3qHB7O6OtQdi9c,54824 +pygame/mouse.pyi,sha256=ngio_X6b9vlY_dEgl6yv6D0ZH5ceACe0mPBjNWl5aBs,731 +pygame/music.pyi,sha256=zsEsgsDk8Nv9qxyfOK7CbbA5z1dyk6F96tldGEX498g,615 +pygame/newbuffer.cpython-37m-x86_64-linux-gnu.so,sha256=8QVsChqmGM4mabS2Bp-rjnpITAzMvorUDIirXBvm6BE,90326 +pygame/pixelarray.cpython-37m-x86_64-linux-gnu.so,sha256=Bf4uFgq8IOdkOqdx_s-p3Wbh8Tz6aJHaxk6aA5tGe0w,202728 +pygame/pixelarray.pyi,sha256=umCx5qq9QTrHIpgeZ0iaZyFxESiAKZpeBZosy10o6z4,1137 +pygame/pixelcopy.cpython-37m-x86_64-linux-gnu.so,sha256=LJ4_QxHRcWYIwwGZQ1AorpYgp1DaaXh2lnSlsMMW37U,99728 +pygame/pixelcopy.pyi,sha256=EckbtZgN8emZz8bJH52TiCv4qItKnWyHtLi1DXAmCRQ,686 +pygame/pkgdata.py,sha256=sN89ew2QZXjAyc1wVlqNSOcPcK4z0cAy9Zmw29qkFoA,2264 +pygame/pygame.ico,sha256=PBF9cw0Ca9Rw2pNmDD3iXvcYYQeI9ZzZ9vxtRLQRoJc,145516 +pygame/pygame_icon.bmp,sha256=Twnby8nv4HMhGka49n-47CPseDvwrSLZ0l1o9U2Bb5s,630 +pygame/pygame_icon.icns,sha256=B3Q59PaET66Br-x3wUFtoymOjJBB9cxEUEA74YE1TL4,53627 +pygame/pygame_icon.svg,sha256=oxge7RESGgP2--7fUmM7HnmD3vadIT5hjDykhVLcIj0,15363 +pygame/pygame_icon.tiff,sha256=cvDqNeR5SckSMyD6a7vNUsVgV7QYXNVWjsmC0BAUX98,61604 +pygame/pypm.cpython-37m-x86_64-linux-gnu.so,sha256=22lG6Q72Pt0G4DmMz1Cfi6hzg6bMUJbhtZFg3ecSwB0,461984 +pygame/rect.cpython-37m-x86_64-linux-gnu.so,sha256=zOAJvfOaeOU9MCoFjL_sL7xae13Fa5f2Zg1vmNBChVU,210648 +pygame/rect.pyi,sha256=4cpulk-d68RCQJhq3G412Y0_7GKmyUfPRKgJtkaI8W4,6771 +pygame/rwobject.cpython-37m-x86_64-linux-gnu.so,sha256=Z_WqCzQZXZQGFyuUI5mkwg_sXPKtaabOvIknIbJ9TX0,89112 +pygame/scrap.cpython-37m-x86_64-linux-gnu.so,sha256=0KTnW5uvDVYOQqJRt0KlEiEY4-vtGJ1a-XowgEeQqR0,63184 +pygame/scrap.pyi,sha256=iBei8wQYnp2UcZ01YyOblEKzkhpWyKr1--NkrTb5vZk,312 +pygame/sndarray.py,sha256=ENLKVipWlflu_xlYlpdUJr5v-kuEcIBp3KjwK7TwI_g,3876 +pygame/sndarray.pyi,sha256=pzi92l9FnvSYVGNo3ZF3_yxSCK99I_-HKiWk3q0wW0A,335 +pygame/sprite.py,sha256=EkrWVY8waNy8cNrXI2HL-aXXh69_rJ07BJN_GidnyA0,56403 +pygame/sprite.pyi,sha256=kQvgj_YRB7cbHK-UO8QZSm0_RBw-iGlE86J0ejQX9jQ,5023 +pygame/surface.cpython-37m-x86_64-linux-gnu.so,sha256=DKc1ypdG8rWEyXgR5UrWHqlTWcHm0odRhPw5eIHRoXw,694760 +pygame/surface.pyi,sha256=TBBqb9EDo3UK2HLikC9kdQ-VwW7T7fSsGjk9kooDaGk,4582 +pygame/surfarray.py,sha256=NSZz-7TxXM_fTIXz-D141PRmdhUbjgzzvtyOMy4QVV8,12302 +pygame/surfarray.pyi,sha256=HIxrPeAznWud7CoHwwwQzynVBaG1MIpcbSzOcDAo_6Y,944 +pygame/surflock.cpython-37m-x86_64-linux-gnu.so,sha256=rXQhgffmR7TqGI4d3PxmnX7uFdg4siL3_4GXfl-TCjM,58680 +pygame/sysfont.py,sha256=vOlxNifY3JaoGBCAjMixfqI3An8r9YiumBxaDzWPrMY,13656 +pygame/tests/__init__.py,sha256=wfUhz-LZF-OXZNT81UfGdofNYPCUMJoF3nwgXpzg4sE,1251 +pygame/tests/__main__.py,sha256=B9PuRd67whv9v1qnCzeIlFzc4R-_9kGj6xwKAS3F8_w,3826 +pygame/tests/__pycache__/__init__.cpython-37.pyc,, +pygame/tests/__pycache__/__main__.cpython-37.pyc,, +pygame/tests/__pycache__/base_test.cpython-37.pyc,, +pygame/tests/__pycache__/blit_test.cpython-37.pyc,, +pygame/tests/__pycache__/bufferproxy_test.cpython-37.pyc,, +pygame/tests/__pycache__/camera_test.cpython-37.pyc,, +pygame/tests/__pycache__/cdrom_tags.cpython-37.pyc,, +pygame/tests/__pycache__/cdrom_test.cpython-37.pyc,, +pygame/tests/__pycache__/color_test.cpython-37.pyc,, +pygame/tests/__pycache__/compat_test.cpython-37.pyc,, +pygame/tests/__pycache__/constants_test.cpython-37.pyc,, +pygame/tests/__pycache__/cursors_test.cpython-37.pyc,, +pygame/tests/__pycache__/display_test.cpython-37.pyc,, +pygame/tests/__pycache__/draw_test.cpython-37.pyc,, +pygame/tests/__pycache__/event_test.cpython-37.pyc,, +pygame/tests/__pycache__/fastevent_tags.cpython-37.pyc,, +pygame/tests/__pycache__/fastevent_test.cpython-37.pyc,, +pygame/tests/__pycache__/font_tags.cpython-37.pyc,, +pygame/tests/__pycache__/font_test.cpython-37.pyc,, +pygame/tests/__pycache__/freetype_tags.cpython-37.pyc,, +pygame/tests/__pycache__/freetype_test.cpython-37.pyc,, +pygame/tests/__pycache__/ftfont_tags.cpython-37.pyc,, +pygame/tests/__pycache__/ftfont_test.cpython-37.pyc,, +pygame/tests/__pycache__/gfxdraw_test.cpython-37.pyc,, +pygame/tests/__pycache__/image__save_gl_surface_test.cpython-37.pyc,, +pygame/tests/__pycache__/image_tags.cpython-37.pyc,, +pygame/tests/__pycache__/image_test.cpython-37.pyc,, +pygame/tests/__pycache__/imageext_tags.cpython-37.pyc,, +pygame/tests/__pycache__/imageext_test.cpython-37.pyc,, +pygame/tests/__pycache__/joystick_test.cpython-37.pyc,, +pygame/tests/__pycache__/key_test.cpython-37.pyc,, +pygame/tests/__pycache__/mask_test.cpython-37.pyc,, +pygame/tests/__pycache__/math_test.cpython-37.pyc,, +pygame/tests/__pycache__/midi_test.cpython-37.pyc,, +pygame/tests/__pycache__/mixer_music_tags.cpython-37.pyc,, +pygame/tests/__pycache__/mixer_music_test.cpython-37.pyc,, +pygame/tests/__pycache__/mixer_tags.cpython-37.pyc,, +pygame/tests/__pycache__/mixer_test.cpython-37.pyc,, +pygame/tests/__pycache__/mouse_test.cpython-37.pyc,, +pygame/tests/__pycache__/overlay_tags.cpython-37.pyc,, +pygame/tests/__pycache__/overlay_test.cpython-37.pyc,, +pygame/tests/__pycache__/pixelarray_test.cpython-37.pyc,, +pygame/tests/__pycache__/pixelcopy_test.cpython-37.pyc,, +pygame/tests/__pycache__/rect_test.cpython-37.pyc,, +pygame/tests/__pycache__/rwobject_test.cpython-37.pyc,, +pygame/tests/__pycache__/scrap_tags.cpython-37.pyc,, +pygame/tests/__pycache__/scrap_test.cpython-37.pyc,, +pygame/tests/__pycache__/sndarray_tags.cpython-37.pyc,, +pygame/tests/__pycache__/sndarray_test.cpython-37.pyc,, +pygame/tests/__pycache__/sprite_test.cpython-37.pyc,, +pygame/tests/__pycache__/surface_test.cpython-37.pyc,, +pygame/tests/__pycache__/surfarray_tags.cpython-37.pyc,, +pygame/tests/__pycache__/surfarray_test.cpython-37.pyc,, +pygame/tests/__pycache__/surflock_test.cpython-37.pyc,, +pygame/tests/__pycache__/sysfont_test.cpython-37.pyc,, +pygame/tests/__pycache__/test_test_.cpython-37.pyc,, +pygame/tests/__pycache__/threads_test.cpython-37.pyc,, +pygame/tests/__pycache__/time_test.cpython-37.pyc,, +pygame/tests/__pycache__/touch_tags.cpython-37.pyc,, +pygame/tests/__pycache__/touch_test.cpython-37.pyc,, +pygame/tests/__pycache__/transform_test.cpython-37.pyc,, +pygame/tests/__pycache__/version_test.cpython-37.pyc,, +pygame/tests/__pycache__/video_test.cpython-37.pyc,, +pygame/tests/base_test.py,sha256=Lt8Wie-oIhgT5fcEz17vOzknhj3sX1NvdsuaI5cVFRU,23518 +pygame/tests/blit_test.py,sha256=ZkKw-4EdR2ELF7GAGx0Fm3iaBV80zoGVma3Mek69YLE,4760 +pygame/tests/bufferproxy_test.py,sha256=UvSokeTLYcqBFtiQsQ1TKXPwd8X2SIsZTEfisInaK2A,16575 +pygame/tests/camera_test.py,sha256=rcfoTOQKD57NaYS7uQ8_IUM45tjH0v-AS9VNrgJ-h6I,129 +pygame/tests/cdrom_tags.py,sha256=P-FDvXmu4xMOMC6wPaL6H5xgl_c34b6Z6EVLuR5d9uU,42 +pygame/tests/cdrom_test.py,sha256=Klyq8zpJNj3xwMkJ-GmdEcBixln330gn4ffh7b8PZlA,9773 +pygame/tests/color_test.py,sha256=aqJA4sFfBeT8zxaSpBne6XV9nGCXrh6EDQiS5m1Jy00,44403 +pygame/tests/compat_test.py,sha256=A4Qim8enmYQlqbxLsqIM3qTG5UrnQy95PM8R2aMqV3w,2751 +pygame/tests/constants_test.py,sha256=yHyrc83rFFzjaKgX2PzU-JdtUV3jaXvVX6A92sNyh8Q,10065 +pygame/tests/cursors_test.py,sha256=yuPSSaacvhMWUaSJabqGCQQ6Hesd0_TSX5cfWEyzuoM,2448 +pygame/tests/display_test.py,sha256=z0vdhJc2-oC2zpMJoFbR1fkahKdmnMY5l-PZtjhJCCs,15292 +pygame/tests/draw_test.py,sha256=okQj1oy3N-LQhXctjnyqLGppZ5M0puOpZHlNeX0hF-w,229799 +pygame/tests/event_test.py,sha256=NQFRIwSG-IxEP5WUKdunJNRr_uszE2RkXQApYu3Md0I,21991 +pygame/tests/fastevent_tags.py,sha256=m9CAhjZFciPRvWFz1fJu6kGZMNt2FrDloEkA_Wz1POg,14 +pygame/tests/fastevent_test.py,sha256=nOxJVL68JKYmUuEjz8fNuLLv0Zxk3fTEmzjrT9N7tWc,5326 +pygame/tests/fixtures/fonts/A_PyGameMono-8.png,sha256=QmhReADwKrzW5RWnG1KHEtZIqpVtwWzhXmydX1su10c,92 +pygame/tests/fixtures/fonts/PyGameMono-18-100dpi.bdf,sha256=nm3okxnfAFtADlp7s2AY43zS49NYg9jq7GVzG2lPhOQ,1947 +pygame/tests/fixtures/fonts/PyGameMono-18-75dpi.bdf,sha256=4kB0uYeEpa3W-ZAomFMpc0hD-h6FnOh2m5IPi6xzfds,1648 +pygame/tests/fixtures/fonts/PyGameMono-8.bdf,sha256=aK0KV-_osDPTPiA1BUCgZHOmufy6J9Vh5pf1IAi0_yg,1365 +pygame/tests/fixtures/fonts/PyGameMono.otf,sha256=_Af4LyMEgKKGa8jDlfik89axhLc3HoS8aG5JHWN5sZw,3128 +pygame/tests/fixtures/fonts/test_fixed.otf,sha256=FWHmFsQUobgtbm370Y5XJv1lAokTreGR5fo4tuw3Who,58464 +pygame/tests/fixtures/fonts/test_sans.ttf,sha256=nrZ6FRet4dwlvA7xOReYCP2QwyGebk0iVJaSFbtpOhM,133088 +pygame/tests/fixtures/fonts/u13079_PyGameMono-8.png,sha256=x_D28PW8aKed8ZHBK6AISEZ9vlEV76Whi770ItTuFVU,89 +pygame/tests/fixtures/xbm_cursors/white_sizing.xbm,sha256=VLAS1A417T-Vg6GMsmicUCYpOhvGsrgJJYUvdFYYteY,366 +pygame/tests/fixtures/xbm_cursors/white_sizing_mask.xbm,sha256=CKQeiOtlFoJdAts83UmTEeVk-3pxgJ9Wu2QJaCjzAQM,391 +pygame/tests/font_tags.py,sha256=m9CAhjZFciPRvWFz1fJu6kGZMNt2FrDloEkA_Wz1POg,14 +pygame/tests/font_test.py,sha256=MqiymgEK87xFuGHHHJkDPnU0T1Ec_3WpPN64p0PoY-Y,19435 +pygame/tests/freetype_tags.py,sha256=NdjMDSYHfrhopKR0JuTeUfFX-AbcCu4fsXnS1a46iVM,182 +pygame/tests/freetype_test.py,sha256=UkMbwxyu2XtPAY7gv9gsjPGmq1vVnZmrp4rGG2byfXM,62770 +pygame/tests/ftfont_tags.py,sha256=IvteBUDEp4rv9q6FwlTpQ9X2px-XUromSMQ921VrhCU,180 +pygame/tests/ftfont_test.py,sha256=-VB-0kdWCDTWVvIf4bRTjrvjvTBZDbfgYZyl20pd2SI,523 +pygame/tests/gfxdraw_test.py,sha256=gdpCUT0nZmwi-9y87y4DXjFaOtLxIis3rcvL-I7Mb9w,32435 +pygame/tests/image__save_gl_surface_test.py,sha256=5H8TeGZNRZzu5kJInWPe8AuuKqHv-utunadoBmn--CI,1198 +pygame/tests/image_tags.py,sha256=_WJGXgTOaUn4IG7fIk1sDKfDDZP3W8N6PkrrOpPT-U8,132 +pygame/tests/image_test.py,sha256=kcBqAwwytoBEsi2qQ31cQJc06OYMxYCkpi9zY1A7qQY,21890 +pygame/tests/imageext_tags.py,sha256=-vnXr7O5F1NVrEDrOHBEYdaD-JiuBT9NI-lxGps-K1U,135 +pygame/tests/imageext_test.py,sha256=XClpp3H1qo1an2Fr8OYoQ4SDO0IhEz6q2gOX09YEvjA,3315 +pygame/tests/joystick_test.py,sha256=f8cwWgnsuksTEGXpzZBpzTXc-C21M6SKHvx0i6zkpNI,3161 +pygame/tests/key_test.py,sha256=gleTmeacvpe3q2xRBQk3wyN0qTHN0Xi42f0Z0-MLiuY,2593 +pygame/tests/mask_test.py,sha256=_Tyw2y4Oxi81yhhTI5Cb2QgwivyH-ftqMsu7LdBqLAM,225376 +pygame/tests/math_test.py,sha256=mlmhtNAY1S5ZR93yc8dh9SoK6X0DieEZZeNvX5CCivU,81757 +pygame/tests/midi_test.py,sha256=MizGFLB3j_kgv6ulJgRoQgoJEuVpUE0vNr5GjCHeqxk,16891 +pygame/tests/mixer_music_tags.py,sha256=o0gsQDjuICFYw8j3WOlIluwk9fdA42ledU1U6DIJzNU,138 +pygame/tests/mixer_music_test.py,sha256=0yEvY2WlJdsN9QvaibexW3_5yHwvPaiYEJGWoDpnkhU,10459 +pygame/tests/mixer_tags.py,sha256=qKcn8AD46H3V87xONG0iXlGH_FveeGBgf2gE1MMh2s0,132 +pygame/tests/mixer_test.py,sha256=WwUQ9JgDYG8MgLg2rUsDibjTrkq5nlqTxmpTgE2oP2A,40752 +pygame/tests/mouse_test.py,sha256=Zv-Zk1d4D1baLDODGVRXy6-AkyF98FIVCv3nJrlvs5Q,3157 +pygame/tests/overlay_tags.py,sha256=FHsQoIZZGZVmyIniGS7w5iMlksfEnYyswqzp64mZ3YE,66 +pygame/tests/overlay_test.py,sha256=w5T2PnIMud1KGdx-0SPwJxpNzDpFt_jHWmmDH4TJd0Y,930 +pygame/tests/pixelarray_test.py,sha256=oV1ECwZ2_Ho45-7I7mIeVHX3jkRznlCpCIFFh_2IGVk,61920 +pygame/tests/pixelcopy_test.py,sha256=12IqgYRpiWJtxklQoYrqm9icx2BTLZI4-Xv0RpcK0VA,25371 +pygame/tests/rect_test.py,sha256=UwVVhDkXFQBeWv14EFoFPu4FRvPKgEMptBa_mJaE45A,73380 +pygame/tests/run_tests__tests/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/__pycache__/__init__.cpython-37.pyc,, +pygame/tests/run_tests__tests/__pycache__/run_tests__test.cpython-37.pyc,, +pygame/tests/run_tests__tests/all_ok/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/all_ok/__pycache__/__init__.cpython-37.pyc,, +pygame/tests/run_tests__tests/all_ok/__pycache__/fake_2_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/all_ok/__pycache__/fake_3_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/all_ok/__pycache__/fake_4_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/all_ok/__pycache__/fake_5_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/all_ok/__pycache__/fake_6_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/all_ok/__pycache__/no_assertions__ret_code_of_1__test.cpython-37.pyc,, +pygame/tests/run_tests__tests/all_ok/__pycache__/zero_tests_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/all_ok/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/all_ok/fake_3_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/all_ok/fake_4_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/all_ok/fake_5_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/all_ok/fake_6_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/all_ok/no_assertions__ret_code_of_1__test.py,sha256=PNrfACCpcPnO964Oxv2-9l4ciuJ-Iqw3x8HDs-kebVg,797 +pygame/tests/run_tests__tests/all_ok/zero_tests_test.py,sha256=XzLaMjkygsvNkFEqnRU9y2Ijm6bfds9n5Z6mg_LOMJQ,545 +pygame/tests/run_tests__tests/everything/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/everything/__pycache__/__init__.cpython-37.pyc,, +pygame/tests/run_tests__tests/everything/__pycache__/fake_2_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/everything/__pycache__/incomplete_todo_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/everything/__pycache__/magic_tag_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/everything/__pycache__/sleep_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/everything/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/everything/incomplete_todo_test.py,sha256=71myeZtFerYY2rB-j60l5Ltz3FiRCuOR4evFXtJHC34,909 +pygame/tests/run_tests__tests/everything/magic_tag_test.py,sha256=SjIKB_7aLfGdih8cotQ34m1KbSEII_1wGQUBwrWeIyY,859 +pygame/tests/run_tests__tests/everything/sleep_test.py,sha256=AyGwZk5fQAkfeCr9VewdsuD_z5BzlVfkmbZD-XetB50,715 +pygame/tests/run_tests__tests/exclude/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/exclude/__pycache__/__init__.cpython-37.pyc,, +pygame/tests/run_tests__tests/exclude/__pycache__/fake_2_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/exclude/__pycache__/invisible_tag_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/exclude/__pycache__/magic_tag_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/exclude/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/exclude/invisible_tag_test.py,sha256=AdHFvOK-kCRi2iUs68So6Ngef6C_LEdx3QpMLjhKtmM,925 +pygame/tests/run_tests__tests/exclude/magic_tag_test.py,sha256=SjIKB_7aLfGdih8cotQ34m1KbSEII_1wGQUBwrWeIyY,859 +pygame/tests/run_tests__tests/failures1/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/failures1/__pycache__/__init__.cpython-37.pyc,, +pygame/tests/run_tests__tests/failures1/__pycache__/fake_2_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/failures1/__pycache__/fake_3_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/failures1/__pycache__/fake_4_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/failures1/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/failures1/fake_3_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/failures1/fake_4_test.py,sha256=xWpIVUpzevSs4bVeze48Q9jkZzss4szdw6eMOrJnZV8,949 +pygame/tests/run_tests__tests/incomplete/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/incomplete/__pycache__/__init__.cpython-37.pyc,, +pygame/tests/run_tests__tests/incomplete/__pycache__/fake_2_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/incomplete/__pycache__/fake_3_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/incomplete/fake_2_test.py,sha256=RVUuQZxqYScIUAflNIsXd7UE6Rxm6HHFZSi8cpz5m-k,889 +pygame/tests/run_tests__tests/incomplete/fake_3_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/incomplete_todo/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/incomplete_todo/__pycache__/__init__.cpython-37.pyc,, +pygame/tests/run_tests__tests/incomplete_todo/__pycache__/fake_2_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/incomplete_todo/__pycache__/fake_3_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/incomplete_todo/fake_2_test.py,sha256=71myeZtFerYY2rB-j60l5Ltz3FiRCuOR4evFXtJHC34,909 +pygame/tests/run_tests__tests/incomplete_todo/fake_3_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/infinite_loop/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/infinite_loop/__pycache__/__init__.cpython-37.pyc,, +pygame/tests/run_tests__tests/infinite_loop/__pycache__/fake_1_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/infinite_loop/__pycache__/fake_2_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/infinite_loop/fake_1_test.py,sha256=rNt-VaNziz7OmfbDXcbXbDIbwC_6ScFJ-MtenMjR68Y,906 +pygame/tests/run_tests__tests/infinite_loop/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/print_stderr/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/print_stderr/__pycache__/__init__.cpython-37.pyc,, +pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_2_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_3_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_4_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/print_stderr/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/print_stderr/fake_3_test.py,sha256=6AGEff135DU_spRhZ09oDGXE4lZC3dlHU_phnfOyWYY,954 +pygame/tests/run_tests__tests/print_stderr/fake_4_test.py,sha256=xWpIVUpzevSs4bVeze48Q9jkZzss4szdw6eMOrJnZV8,949 +pygame/tests/run_tests__tests/print_stdout/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/print_stdout/__pycache__/__init__.cpython-37.pyc,, +pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_2_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_3_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_4_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/print_stdout/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/print_stdout/fake_3_test.py,sha256=cruYqrh3O3MQ8fczEFloLpsrQrYmMOd6jgxMU6e5H8w,1012 +pygame/tests/run_tests__tests/print_stdout/fake_4_test.py,sha256=xWpIVUpzevSs4bVeze48Q9jkZzss4szdw6eMOrJnZV8,949 +pygame/tests/run_tests__tests/run_tests__test.py,sha256=oM-sBRUtv5QkCJ49H-LtuSLIWsX_u0_f8LeVDczZqN8,4342 +pygame/tests/run_tests__tests/timeout/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/timeout/__pycache__/__init__.cpython-37.pyc,, +pygame/tests/run_tests__tests/timeout/__pycache__/fake_2_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/timeout/__pycache__/sleep_test.cpython-37.pyc,, +pygame/tests/run_tests__tests/timeout/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/timeout/sleep_test.py,sha256=5EDW4U6kYN4QIid0IgHBypJ3T3a78pILILF41DPpujk,716 +pygame/tests/rwobject_test.py,sha256=DGJ0fkPbNZwVI7UNWlAfM5d_UxFOjEV5fHcaHLf_3so,4257 +pygame/tests/scrap_tags.py,sha256=8cBca2lbvONKNHFKIHP4vZ3UAAUOXB4p5LEwsZAaBds,414 +pygame/tests/scrap_test.py,sha256=0lSryLACPR9fKkydnoPBE3cGiUxvXoBjbTRTc-4SNEo,9270 +pygame/tests/sndarray_tags.py,sha256=ThDQxqGFaAembuWgdYGsFSWEppVezgXJ2htYRvvDaXE,190 +pygame/tests/sndarray_test.py,sha256=bfSp1uXNZyrvQToauVg6HTN8wxzSH0OmGSetIIRIzvg,6485 +pygame/tests/sprite_test.py,sha256=tidvrG5tdt-9Y5H1Ceahsql2tmMFLgDJOaJg9RbPWZ4,44801 +pygame/tests/surface_test.py,sha256=liU0i7WN2brRAL6_MH0mQpbXAozsaSSJUqIxB8L8Wmw,101245 +pygame/tests/surfarray_tags.py,sha256=AwlglKM7DrjHvvcSMm-yXb-PSxsVhJkS6VE7Z8wOhes,260 +pygame/tests/surfarray_test.py,sha256=Xc5Zx42-jAF7xrU1M0eBEu3neB4JrqwMOkLtO6xkEtw,25325 +pygame/tests/surflock_test.py,sha256=dMZkzND7-R_z-GaxN4ZIcpNtW3PsK1i7kdnizpU21UY,4728 +pygame/tests/sysfont_test.py,sha256=_ra-01fy_DsgmdvbnBtyYPeVdct1TK78cox4f9w6NEI,802 +pygame/tests/test_test_.py,sha256=2LXEtnUdSV5f_vU-SvIYka21ANoN8o3BlyfKDNohPYs,21 +pygame/tests/test_utils/__init__.py,sha256=qzieVXjLnLo20tnITUsOk42tn1P50nBHg4XfUDQFkks,5971 +pygame/tests/test_utils/__pycache__/__init__.cpython-37.pyc,, +pygame/tests/test_utils/__pycache__/arrinter.cpython-37.pyc,, +pygame/tests/test_utils/__pycache__/async_sub.cpython-37.pyc,, +pygame/tests/test_utils/__pycache__/buftools.cpython-37.pyc,, +pygame/tests/test_utils/__pycache__/endian.cpython-37.pyc,, +pygame/tests/test_utils/__pycache__/png.cpython-37.pyc,, +pygame/tests/test_utils/__pycache__/run_tests.cpython-37.pyc,, +pygame/tests/test_utils/__pycache__/test_machinery.cpython-37.pyc,, +pygame/tests/test_utils/__pycache__/test_runner.cpython-37.pyc,, +pygame/tests/test_utils/arrinter.py,sha256=Odb-1N1hcnQdsTbr9UcPdQk8Bg1M-AzwhkpFhhq6ACU,15234 +pygame/tests/test_utils/async_sub.py,sha256=j3-9yRbBi7YX0wyA7uZjjdAgCOYSGCsW8Bnuqhp_HsY,9413 +pygame/tests/test_utils/buftools.py,sha256=RnbYFXWg3WWC4YJFbKpccfcDqjyjISrvBMXW-HnBLDs,23715 +pygame/tests/test_utils/endian.py,sha256=Rc7rl38YamHgi8EzB92Muu8C4XH6yltH9f5On7qfMpY,495 +pygame/tests/test_utils/png.py,sha256=JAT-3uXWhn3tMEGfex8AU7MFbrJSE6F2GdeXrjDJVeg,152408 +pygame/tests/test_utils/run_tests.py,sha256=4l3Oia_V6XqNDDkyy03CqEQTnfn1csSzSJpDS8Pdm_M,12247 +pygame/tests/test_utils/test_machinery.py,sha256=WcbORRvQPnKXvNWq10tqD92xjVoSIUhdieEh2ozTQ1c,2483 +pygame/tests/test_utils/test_runner.py,sha256=77tkKp5D5VFfqpgsFCU_XgI0Y03j_cntbDX_YuNRJ-8,9451 +pygame/tests/threads_test.py,sha256=BVIfmT7RqeI0WINZi6jG44TgBXZ8KPWYqcO-dj36jaE,5056 +pygame/tests/time_test.py,sha256=0dbsY_-SK7_x91KvSXdpGS8B9QBWR0LLJe35MPIVIZU,6733 +pygame/tests/touch_tags.py,sha256=hm30jqrBdP7i9MlYe8qPRjs0wSJ7y8NKiuytQU_7Heg,27 +pygame/tests/touch_test.py,sha256=vOEZA5By_b_v8xiFrOGei4_d6NS_KSFXOpBy3Tju5UA,1197 +pygame/tests/transform_test.py,sha256=lZHAcu6TRsx0NVbmsAJfhjptGWq-8osrTSgOH2VPx3E,40468 +pygame/tests/version_test.py,sha256=2sDyF4ZPasOzk_JbzCfwXWdN-XjZgwND7g0UYLc-Rzg,1421 +pygame/tests/video_test.py,sha256=wvHWnnpBaR5QSg3-bz86MShDs6i9xhu3m0bmnso0VEk,586 +pygame/threads/Py25Queue.py,sha256=qdHdnydu26pfhdq7VRGD8j8cFN2DdYuxImUGdzLWhG0,7759 +pygame/threads/__init__.py,sha256=FM7nVs5Y6XnDRR1QcDCglMjO-BZjf_0Zx3TXRqJUyls,8701 +pygame/threads/__pycache__/Py25Queue.cpython-37.pyc,, +pygame/threads/__pycache__/__init__.cpython-37.pyc,, +pygame/time.cpython-37m-x86_64-linux-gnu.so,sha256=zwY49Ka1PyutCdQqOXgyoqftTRAMaVd0nE-xvPP_gaw,75216 +pygame/time.pyi,sha256=cevPwhUSUrhldHVhXXJH1tQwk1YqahtCvYUqgmxYBWA,544 +pygame/transform.cpython-37m-x86_64-linux-gnu.so,sha256=sFJzPw02fi5fRn5Si4MwKnWCpB0iS15LIHgZsp7qvTI,260192 +pygame/transform.pyi,sha256=9zLuMVTCOLRrl3r_GNxcONU-buud5tA0JNASs9gHpr4,1874 +pygame/version.py,sha256=ZYLj4kYemb18txKkHSrBkXRAg1DIcBze8jB-VKcizIA,1961 +pygame/version.pyi,sha256=elhaimInr4aFjV_wc4j_fdNtHioJ6XWihCs8yrbsS7U,73 diff --git a/Display/.venv/lib/python3.7/site-packages/pygame-2.0.0.dev8.dist-info/WHEEL b/Display/.venv/lib/python3.7/site-packages/pygame-2.0.0.dev8.dist-info/WHEEL new file mode 100644 index 0000000..697e432 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame-2.0.0.dev8.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.31.1) +Root-Is-Purelib: false +Tag: cp37-cp37m-manylinux1_x86_64 + diff --git a/Display/.venv/lib/python3.7/site-packages/pygame-2.0.0.dev8.dist-info/top_level.txt b/Display/.venv/lib/python3.7/site-packages/pygame-2.0.0.dev8.dist-info/top_level.txt new file mode 100644 index 0000000..0cb7ff1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame-2.0.0.dev8.dist-info/top_level.txt @@ -0,0 +1 @@ +pygame diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libFLAC-bf6d1292.so.8.3.0 b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libFLAC-bf6d1292.so.8.3.0 new file mode 100755 index 0000000..04871ba Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libFLAC-bf6d1292.so.8.3.0 differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libSDL2-2-a810f3c1.0.so.0.12.0 b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libSDL2-2-a810f3c1.0.so.0.12.0 new file mode 100755 index 0000000..bbf3265 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libSDL2-2-a810f3c1.0.so.0.12.0 differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libSDL2_image-2-4bd2d50c.0.so.0.2.3 b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libSDL2_image-2-4bd2d50c.0.so.0.2.3 new file mode 100755 index 0000000..397514a Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libSDL2_image-2-4bd2d50c.0.so.0.2.3 differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libSDL2_mixer-2-ee256d43.0.so.0.2.2 b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libSDL2_mixer-2-ee256d43.0.so.0.2.2 new file mode 100755 index 0000000..1c5f793 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libSDL2_mixer-2-ee256d43.0.so.0.2.2 differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libSDL2_ttf-2-9c5a5e5f.0.so.0.14.1 b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libSDL2_ttf-2-9c5a5e5f.0.so.0.14.1 new file mode 100755 index 0000000..3a0d1a6 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libSDL2_ttf-2-9c5a5e5f.0.so.0.14.1 differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libasound-87b0bd31.so.2.0.0 b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libasound-87b0bd31.so.2.0.0 new file mode 100755 index 0000000..b6104ed Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libasound-87b0bd31.so.2.0.0 differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libfreetype-2d39c124.so.6.17.1 b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libfreetype-2d39c124.so.6.17.1 new file mode 100755 index 0000000..d69d25e Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libfreetype-2d39c124.so.6.17.1 differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libjpeg-bd53fca1.so.62.0.0 b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libjpeg-bd53fca1.so.62.0.0 new file mode 100755 index 0000000..fb21c25 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libjpeg-bd53fca1.so.62.0.0 differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libmikmod-fabcac29.so.2.0.4 b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libmikmod-fabcac29.so.2.0.4 new file mode 100755 index 0000000..e2c2752 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libmikmod-fabcac29.so.2.0.4 differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libogg-b51fbe74.so.0.8.4 b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libogg-b51fbe74.so.0.8.4 new file mode 100755 index 0000000..e4964e1 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libogg-b51fbe74.so.0.8.4 differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libpng16-b14e7f97.so.16.37.0 b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libpng16-b14e7f97.so.16.37.0 new file mode 100755 index 0000000..8a61592 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libpng16-b14e7f97.so.16.37.0 differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libportmidi-7fcf6c23.so b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libportmidi-7fcf6c23.so new file mode 100755 index 0000000..59a8992 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libportmidi-7fcf6c23.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libtiff-97e44e95.so.3.8.2 b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libtiff-97e44e95.so.3.8.2 new file mode 100755 index 0000000..ea7fd4f Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libtiff-97e44e95.so.3.8.2 differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libvorbis-205f0f59.so.0.4.8 b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libvorbis-205f0f59.so.0.4.8 new file mode 100755 index 0000000..52be580 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libvorbis-205f0f59.so.0.4.8 differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libvorbisfile-f207f3a6.so.3.3.7 b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libvorbisfile-f207f3a6.so.3.3.7 new file mode 100755 index 0000000..a33cd35 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libvorbisfile-f207f3a6.so.3.3.7 differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libwebp-582c46b3.so.7.1.0 b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libwebp-582c46b3.so.7.1.0 new file mode 100755 index 0000000..45f4767 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libwebp-582c46b3.so.7.1.0 differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libz-a147dcb0.so.1.2.3 b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libz-a147dcb0.so.1.2.3 new file mode 100755 index 0000000..1ce02c2 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/.libs/libz-a147dcb0.so.1.2.3 differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/__init__.py new file mode 100644 index 0000000..fbbe8ca --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/__init__.py @@ -0,0 +1,363 @@ +# coding: ascii +# pygame - Python Game Library +# Copyright (C) 2000-2001 Pete Shinners +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Pete Shinners +# pete@shinners.org +"""Pygame is a set of Python modules designed for writing games. +It is written on top of the excellent SDL library. This allows you +to create fully featured games and multimedia programs in the python +language. The package is highly portable, with games running on +Windows, MacOS, OS X, BeOS, FreeBSD, IRIX, and Linux.""" + +import sys +import os + +# Choose Windows display driver +if os.name == 'nt': + #pypy does not find the dlls, so we add package folder to PATH. + pygame_dir = os.path.split(__file__)[0] + os.environ['PATH'] = os.environ['PATH'] + ';' + pygame_dir + +# when running under X11, always set the SDL window WM_CLASS to make the +# window managers correctly match the pygame window. +elif 'DISPLAY' in os.environ and 'SDL_VIDEO_X11_WMCLASS' not in os.environ: + os.environ['SDL_VIDEO_X11_WMCLASS'] = os.path.basename(sys.argv[0]) + + +class MissingModule: + _NOT_IMPLEMENTED_ = True + + def __init__(self, name, urgent=0): + self.name = name + exc_type, exc_msg = sys.exc_info()[:2] + self.info = str(exc_msg) + self.reason = "%s: %s" % (exc_type.__name__, self.info) + self.urgent = urgent + if urgent: + self.warn() + + def __getattr__(self, var): + if not self.urgent: + self.warn() + self.urgent = 1 + missing_msg = "%s module not available (%s)" % (self.name, self.reason) + raise NotImplementedError(missing_msg) + + def __nonzero__(self): + return 0 + + def warn(self): + msg_type = 'import' if self.urgent else 'use' + message = '%s %s: %s\n(%s)' % (msg_type, self.name, self.info, self.reason) + try: + import warnings + level = 4 if self.urgent else 3 + warnings.warn(message, RuntimeWarning, level) + except ImportError: + print (message) + + +# we need to import like this, each at a time. the cleanest way to import +# our modules is with the import command (not the __import__ function) + +# first, the "required" modules +from pygame.base import * +from pygame.constants import * +from pygame.version import * +from pygame.rect import Rect +from pygame.compat import PY_MAJOR_VERSION +from pygame.rwobject import encode_string, encode_file_path +import pygame.surflock +import pygame.color +Color = color.Color +import pygame.bufferproxy +BufferProxy = bufferproxy.BufferProxy +import pygame.math +Vector2 = pygame.math.Vector2 +Vector3 = pygame.math.Vector3 + +__version__ = ver + +# next, the "standard" modules +# we still allow them to be missing for stripped down pygame distributions +if get_sdl_version() < (2, 0, 0): + # cdrom only available for SDL 1.2.X + try: + import pygame.cdrom + except (ImportError, IOError): + cdrom = MissingModule("cdrom", urgent=1) + +try: + import pygame.cursors +except (ImportError, IOError): + cursors = MissingModule("cursors", urgent=1) + +try: + import pygame.display +except (ImportError, IOError): + display = MissingModule("display", urgent=1) + +try: + import pygame.draw +except (ImportError, IOError): + draw = MissingModule("draw", urgent=1) + +try: + import pygame.event +except (ImportError, IOError): + event = MissingModule("event", urgent=1) + +try: + import pygame.image +except (ImportError, IOError): + image = MissingModule("image", urgent=1) + +try: + import pygame.joystick +except (ImportError, IOError): + joystick = MissingModule("joystick", urgent=1) + +try: + import pygame.key +except (ImportError, IOError): + key = MissingModule("key", urgent=1) + +try: + import pygame.mouse +except (ImportError, IOError): + mouse = MissingModule("mouse", urgent=1) + +try: + import pygame.sprite +except (ImportError, IOError): + sprite = MissingModule("sprite", urgent=1) + +try: + import pygame.threads +except (ImportError, IOError): + threads = MissingModule("threads", urgent=1) + +try: + import pygame.pixelcopy +except (ImportError, IOError): + pixelcopy = MissingModule("pixelcopy", urgent=1) + + +def warn_unwanted_files(): + """warn about unneeded old files""" + + # a temporary hack to warn about camera.so and camera.pyd. + install_path = os.path.split(pygame.base.__file__)[0] + extension_ext = os.path.splitext(pygame.base.__file__)[1] + + # here are the .so/.pyd files we need to ask to remove. + ext_to_remove = ["camera"] + + # here are the .py/.pyo/.pyc files we need to ask to remove. + py_to_remove = ["color"] + + # Don't warn on Symbian. The color.py is used as a wrapper. + if os.name == "e32": + py_to_remove = [] + + # See if any of the files are there. + extension_files = ["%s%s" % (x, extension_ext) for x in ext_to_remove] + + py_files = ["%s%s" % (x, py_ext) + for py_ext in [".py", ".pyc", ".pyo"] + for x in py_to_remove] + + files = py_files + extension_files + + unwanted_files = [] + for f in files: + unwanted_files.append(os.path.join(install_path, f)) + + ask_remove = [] + for f in unwanted_files: + if os.path.exists(f): + ask_remove.append(f) + + if ask_remove: + message = "Detected old file(s). Please remove the old files:\n" + + for f in ask_remove: + message += "%s " % f + message += "\nLeaving them there might break pygame. Cheers!\n\n" + + try: + import warnings + level = 4 + warnings.warn(message, RuntimeWarning, level) + except ImportError: + print (message) + + +# disable, because we hopefully don't need it. +# warn_unwanted_files() + + +try: + from pygame.surface import Surface, SurfaceType +except (ImportError, IOError): + Surface = lambda: Missing_Function + + +try: + import pygame.mask + from pygame.mask import Mask +except (ImportError, IOError): + Mask = lambda: Missing_Function + +try: + from pygame.pixelarray import PixelArray +except (ImportError, IOError): + PixelArray = lambda: Missing_Function + +try: + from pygame.overlay import Overlay +except (ImportError, IOError): + Overlay = lambda: Missing_Function + +try: + import pygame.time +except (ImportError, IOError): + time = MissingModule("time", urgent=1) + +try: + import pygame.transform +except (ImportError, IOError): + transform = MissingModule("transform", urgent=1) + +# lastly, the "optional" pygame modules +if 'PYGAME_FREETYPE' in os.environ: + try: + import pygame.ftfont as font + sys.modules['pygame.font'] = font + except (ImportError, IOError): + pass +try: + import pygame.font + import pygame.sysfont + pygame.font.SysFont = pygame.sysfont.SysFont + pygame.font.get_fonts = pygame.sysfont.get_fonts + pygame.font.match_font = pygame.sysfont.match_font +except (ImportError, IOError): + font = MissingModule("font", urgent=0) + +# try and load pygame.mixer_music before mixer, for py2app... +try: + import pygame.mixer_music + #del pygame.mixer_music + #print ("NOTE2: failed importing pygame.mixer_music in lib/__init__.py") +except (ImportError, IOError): + pass + +try: + import pygame.mixer +except (ImportError, IOError): + mixer = MissingModule("mixer", urgent=0) + +try: + import pygame.movie +except (ImportError, IOError): + movie = MissingModule("movie", urgent=0) + +# try: +# import pygame.movieext +# except (ImportError,IOError): +# movieext=MissingModule("movieext", urgent=0) + +try: + import pygame.scrap +except (ImportError, IOError): + scrap = MissingModule("scrap", urgent=0) + +try: + import pygame.surfarray +except (ImportError, IOError): + surfarray = MissingModule("surfarray", urgent=0) + +try: + import pygame.sndarray +except (ImportError, IOError): + sndarray = MissingModule("sndarray", urgent=0) + +try: + import pygame.fastevent +except (ImportError, IOError): + fastevent = MissingModule("fastevent", urgent=0) + +# there's also a couple "internal" modules not needed +# by users, but putting them here helps "dependency finder" +# programs get everything they need (like py2exe) +try: + import pygame.imageext + del pygame.imageext +except (ImportError, IOError): + pass + + +def packager_imports(): + """some additional imports that py2app/py2exe will want to see""" + import atexit + import numpy + import OpenGL.GL + import pygame.macosx + import pygame.bufferproxy + import pygame.colordict + import pygame._view + +# make Rects pickleable +if PY_MAJOR_VERSION >= 3: + import copyreg as copy_reg +else: + import copy_reg + + +def __rect_constructor(x, y, w, h): + return Rect(x, y, w, h) + + +def __rect_reduce(r): + assert type(r) == Rect + return __rect_constructor, (r.x, r.y, r.w, r.h) +copy_reg.pickle(Rect, __rect_reduce, __rect_constructor) + + +# make Colors pickleable +def __color_constructor(r, g, b, a): + return Color(r, g, b, a) + + +def __color_reduce(c): + assert type(c) == Color + return __color_constructor, (c.r, c.g, c.b, c.a) +copy_reg.pickle(Color, __color_reduce, __color_constructor) + + +# Thanks for supporting pygame. Without support now, there won't be pygame later. +if 'PYGAME_HIDE_SUPPORT_PROMPT' not in os.environ: + print('pygame {} (SDL {}.{}.{}, python {}.{}.{})'.format( + ver, *get_sdl_version() + sys.version_info[0:3] + )) + print('Hello from the pygame community. https://www.pygame.org/contribute.html') + + +# cleanup namespace +del pygame, os, sys, surflock, MissingModule, copy_reg, PY_MAJOR_VERSION diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/__init__.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/__init__.pyi new file mode 100644 index 0000000..fcdcac8 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/__init__.pyi @@ -0,0 +1,72 @@ +from typing import Any, Tuple, Callable, Union, Optional, overload, Type + +# Most useful stuff +from pygame.constants import * +import pygame.surface +import pygame.rect +import pygame.color +import pygame.event +import pygame.bufferproxy +import pygame.draw +import pygame.display +import pygame.font +import pygame.image +import pygame.key +import pygame.mixer +import pygame.mouse +import pygame.time +import pygame.version + +# Advanced stuff +import pygame.cursors +import pygame.joystick +import pygame.mask +import pygame.sprite +import pygame.transform +import pygame.bufferproxy +import pygame.pixelarray +import pygame.pixelcopy +import pygame.sndarray +import pygame.surfarray +import pygame.math +import pygame.fastevent + +# Other +import pygame.scrap + +# This classes are auto imported with pygame, so I put their declaration here +class Rect(pygame.rect.Rect): ... +class Surface(pygame.surface.Surface): ... +class Color(pygame.color.Color): ... +class PixelArray(pygame.pixelarray.PixelArray): ... +class Vector2(pygame.math.Vector2): ... +class Vector3(pygame.math.Vector3): ... + + +def init() -> Tuple[int, int]: ... +def quit() -> None: ... +def get_init() -> bool: ... + +class error(RuntimeError): + RuntimeError + +def get_error() -> str: ... +def set_error(error_msg: str) -> None: ... +def get_sdl_version() -> Tuple[int, int, int]: ... +def get_sdl_byteorder() -> int: ... +def encode_string( + obj: Union[str, bytes], + encoding: Optional[str] = "unicode_escape", + errors: Optional[str] = "backslashreplace", + etype: Optional[Type[Exception]] = UnicodeEncodeError, +) -> bytes: ... +@overload +def encode_file_path( + obj: Union[str, bytes], etype: Optional[Type[Exception]] = UnicodeEncodeError +) -> bytes: ... +@overload +def encode_file_path( + obj: Any, etype: Optional[Type[Exception]] = UnicodeEncodeError +) -> bytes: ... +def register_quit(callable: Callable) -> None: ... +def __getattr__(name) -> Any: ... # don't error on missing stubs diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/_camera.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/_camera.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..9e307a9 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/_camera.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/_camera_opencv_highgui.py b/Display/.venv/lib/python3.7/site-packages/pygame/_camera_opencv_highgui.py new file mode 100644 index 0000000..6acf190 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/_camera_opencv_highgui.py @@ -0,0 +1,98 @@ + +import pygame +import numpy + +import opencv +#this is important for capturing/displaying images +from opencv import highgui + + + +def list_cameras(): + """ + """ + # -1 for opencv means get any of them. + return [-1] + +def init(): + pass + +def quit(): + pass + + +class Camera: + + def __init__(self, device = 0, size = (640,480), mode = "RGB"): + """ + """ + self.camera = highgui.cvCreateCameraCapture(device) + if not self.camera: + raise ValueError ("Could not open camera. Sorry.") + + + def set_controls(self, **kwargs): + """ + """ + + + def set_resolution(self, width, height): + """Sets the capture resolution. (without dialog) + """ + # nothing to do here. + pass + def query_image(self): + return True + + def stop(self): + pass + + def start(self): + # do nothing here... since the camera is already open. + pass + + def get_buffer(self): + """Returns a string containing the raw pixel data. + """ + return self.get_surface().get_buffer() + + def get_image(self, dest_surf = None): + return self.get_surface(dest_surf) + + def get_surface(self, dest_surf = None): + camera = self.camera + + im = highgui.cvQueryFrame(camera) + #convert Ipl image to PIL image + #print type(im) + if im: + xx = opencv.adaptors.Ipl2NumPy(im) + #print type(xx) + #print xx.iscontiguous() + #print dir(xx) + #print xx.shape + xxx = numpy.reshape(xx, (numpy.product(xx.shape),)) + + if xx.shape[2] != 3: + raise ValueError("not sure what to do about this size") + + pg_img = pygame.image.frombuffer(xxx, (xx.shape[1],xx.shape[0]), "RGB") + + # if there is a destination surface given, we blit onto that. + if dest_surf: + dest_surf.blit(pg_img, (0,0)) + return dest_surf + #return pg_img + + + +if __name__ == "__main__": + + # try and use this camera stuff with the pygame camera example. + import pygame.examples.camera + + pygame.camera.Camera = Camera + pygame.camera.list_cameras = list_cameras + pygame.examples.camera.main() + + diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/_camera_vidcapture.py b/Display/.venv/lib/python3.7/site-packages/pygame/_camera_vidcapture.py new file mode 100644 index 0000000..82988d2 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/_camera_vidcapture.py @@ -0,0 +1,131 @@ +"""pygame.camera.Camera implementation using the videocapture module for windows. + +http://videocapture.sourceforge.net/ + +Binary windows wheels: + https://www.lfd.uci.edu/~gohlke/pythonlibs/#videocapture +""" +import pygame + +def list_cameras(): + """Always only lists one camera. + + Functionality not supported in videocapture module. + """ + return [0] + + # this just cycles through all the cameras trying to open them + #cameras = [] + #for x in range(256): + # try: + # c = Camera(x) + # except: + # break + # cameras.append(x) + #return cameras + +def init(): + global vidcap + try: + import vidcap as vc + except ImportError: + from VideoCapture import vidcap as vc + vidcap = vc + +def quit(): + global vidcap + vidcap = None + + +class Camera: + + def __init__(self, device =0, + size = (640,480), + mode = "RGB", + show_video_window=0): + """device: VideoCapture enumerates the available video capture devices + on your system. If you have more than one device, specify + the desired one here. The device number starts from 0. + + show_video_window: 0 ... do not display a video window (the default) + 1 ... display a video window + + Mainly used for debugging, since the video window + can not be closed or moved around. + """ + self.dev = vidcap.new_Dev(device, show_video_window) + width, height = size + self.dev.setresolution(width, height) + + def display_capture_filter_properties(self): + """Displays a dialog containing the property page of the capture filter. + + For VfW drivers you may find the option to select the resolution most + likely here. + """ + self.dev.displaycapturefilterproperties() + + def display_capture_pin_properties(self): + """Displays a dialog containing the property page of the capture pin. + + For WDM drivers you may find the option to select the resolution most + likely here. + """ + self.dev.displaycapturepinproperties() + + def set_resolution(self, width, height): + """Sets the capture resolution. (without dialog) + """ + self.dev.setresolution(width, height) + + def get_buffer(self): + """Returns a string containing the raw pixel data. + """ + return self.dev.getbuffer() + + def start(self): + """ Not implemented. + """ + + def set_controls(self, **kwargs): + """ Not implemented. + """ + + def stop(self): + """ Not implemented. + """ + + def get_image(self, dest_surf = None): + """ + """ + return self.get_surface(dest_surf) + + def get_surface(self, dest_surf = None): + """Returns a pygame Surface. + """ + abuffer, width, height = self.get_buffer() + if abuffer: + surf = pygame.image.frombuffer(abuffer, (width, height), "RGB") + + # swap it from a BGR surface to an RGB surface. + r,g,b,a = surf.get_masks() + surf.set_masks((b,g,r,a)) + + r,g,b,a = surf.get_shifts() + surf.set_shifts((b,g,r,a)) + + surf = pygame.transform.flip(surf, 0,1) + + # if there is a destination surface given, we blit onto that. + if dest_surf: + dest_surf.blit(surf, (0,0)) + else: + dest_surf = surf + return dest_surf + +if __name__ == "__main__": + import pygame.examples.camera + + pygame.camera.Camera = Camera + pygame.camera.list_cameras = list_cameras + pygame.examples.camera.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/_dummybackend.py b/Display/.venv/lib/python3.7/site-packages/pygame/_dummybackend.py new file mode 100644 index 0000000..49b3e30 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/_dummybackend.py @@ -0,0 +1,30 @@ +"""dummy Movie class if all else fails """ +class Movie: + def __init__(self, filename, surface=None): + self.filename=filename + self.surface = surface + self.process = None + self.loops=0 + self.playing = False + self.paused = False + self._backend = "DUMMY" + self.width=0 + self.height=0 + self.finished = 1 + def play(self, loops=0): + self.playing= not self.playing + + def stop(self): + self.playing=not self.playing + self.paused =not self.paused + + def pause(self): + self.paused=not self.paused + + def resize(self, w, h): + self.width=w + self.height=h + + def __repr__(self): + return "(%s 0.0s)"%self.filename + \ No newline at end of file diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/_freetype.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/_freetype.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..a99857f Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/_freetype.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/_numpysndarray.py b/Display/.venv/lib/python3.7/site-packages/pygame/_numpysndarray.py new file mode 100644 index 0000000..fc8e1fa --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/_numpysndarray.py @@ -0,0 +1,75 @@ +## pygame - Python Game Library +## Copyright (C) 2008 Marcus von Appen +## +## This library is free software; you can redistribute it and/or +## modify it under the terms of the GNU Library General Public +## License as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## Library General Public License for more details. +## +## You should have received a copy of the GNU Library General Public +## License along with this library; if not, write to the Free +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## +## Marcus von Appen +## mva@sysfault.org + +"""pygame module for accessing sound sample data using numpy + +Functions to convert between numpy arrays and Sound objects. This module +will only be available when pygame can use the external numpy package. + +Sound data is made of thousands of samples per second, and each sample +is the amplitude of the wave at a particular moment in time. For +example, in 22-kHz format, element number 5 of the array is the +amplitude of the wave after 5/22000 seconds. + +Each sample is an 8-bit or 16-bit integer, depending on the data format. +A stereo sound file has two values per sample, while a mono sound file +only has one. +""" + +import pygame.mixer as mixer +import numpy + + +def array (sound): + """pygame._numpysndarray.array(Sound): return array + + Copy Sound samples into an array. + + Creates a new array for the sound data and copies the samples. The + array will always be in the format returned from + pygame.mixer.get_init(). + """ + + return numpy.array (sound, copy=True) + +def samples (sound): + """pygame._numpysndarray.samples(Sound): return array + + Reference Sound samples into an array. + + Creates a new array that directly references the samples in a Sound + object. Modifying the array will change the Sound. The array will + always be in the format returned from pygame.mixer.get_init(). + """ + + return numpy.array (sound, copy=False) + +def make_sound (array): + """pygame._numpysndarray.make_sound(array): return Sound + + Convert an array into a Sound object. + + Create a new playable Sound object from an array. The mixer module + must be initialized and the array format must be similar to the mixer + audio format. + """ + + return mixer.Sound (array=array) + diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/_numpysurfarray.py b/Display/.venv/lib/python3.7/site-packages/pygame/_numpysurfarray.py new file mode 100644 index 0000000..a2371ab --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/_numpysurfarray.py @@ -0,0 +1,353 @@ +## pygame - Python Game Library +## Copyright (C) 2007 Marcus von Appen +## +## This library is free software; you can redistribute it and/or +## modify it under the terms of the GNU Library General Public +## License as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## Library General Public License for more details. +## +## You should have received a copy of the GNU Library General Public +## License along with this library; if not, write to the Free +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## +## Marcus von Appen +## mva@sysfault.org + +"""pygame module for accessing surface pixel data using numpy + +Functions to convert pixel data between pygame Surfaces and Numpy +arrays. This module will only be available when pygame can use the +external Numpy package. + +Note, that numpyarray is an optional module. It requires that Numpy is +installed to be used. If not installed, an exception will be raised when +it is used. eg. ImportError: no module named numpy + +Every pixel is stored as a single integer value to represent the red, +green, and blue colors. The 8bit images use a value that looks into a +colormap. Pixels with higher depth use a bit packing process to place +three or four values into a single number. + +The Numpy arrays are indexed by the X axis first, followed by the Y +axis. Arrays that treat the pixels as a single integer are referred to +as 2D arrays. This module can also separate the red, green, and blue +color values into separate indices. These types of arrays are referred +to as 3D arrays, and the last index is 0 for red, 1 for green, and 2 for +blue. + +In contrast to Numeric Numpy does use unsigned 16bit integers, images +with 16bit data will be treated as unsigned integers. +""" + +from pygame.pixelcopy import array_to_surface, surface_to_array, \ + map_array as pix_map_array, make_surface as pix_make_surface +import numpy +from numpy import array as numpy_array, empty as numpy_empty, \ + uint32 as numpy_uint32, ndarray as numpy_ndarray + +#float96 not available on all numpy versions. +numpy_floats = [] +for type_name in "float float32 float64 float96".split(): + if hasattr(numpy, type_name): + numpy_floats.append(getattr(numpy, type_name)) + +# Pixel sizes corresponding to NumPy supported integer sizes, and therefore +# permissible for 2D reference arrays. +_pixel2d_bitdepths = set([8, 16, 32]) + + +def blit_array (surface, array): + """pygame.surfarray.blit_array(Surface, array): return None + + Blit directly from a array values. + + Directly copy values from an array into a Surface. This is faster than + converting the array into a Surface and blitting. The array must be the + same dimensions as the Surface and will completely replace all pixel + values. Only integer, ascii character and record arrays are accepted. + + This function will temporarily lock the Surface as the new values are + copied. + """ + if isinstance(array, numpy_ndarray) and array.dtype in numpy_floats: + array = array.round(0).astype(numpy_uint32) + return array_to_surface(surface, array) + +def make_surface(array): + """pygame.surfarray.make_surface (array): return Surface + + Copy an array to a new surface. + + Create a new Surface that best resembles the data and format on the + array. The array can be 2D or 3D with any sized integer values. + """ + if isinstance(array, numpy_ndarray) and array.dtype in numpy_floats: + array = array.round(0).astype(numpy_uint32) + return pix_make_surface (array) + +def array2d(surface): + """pygame.numpyarray.array2d(Surface): return array + + copy pixels into a 2d array + + Copy the pixels from a Surface into a 2D array. The bit depth of the + surface will control the size of the integer values, and will work + for any type of pixel format. + + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + bpp = surface.get_bytesize() + try: + dtype = (numpy.uint8, numpy.uint16, numpy.int32, numpy.int32)[bpp - 1] + except IndexError: + raise ValueError("unsupported bit depth %i for 2D array" % (bpp * 8,)) + size = surface.get_size() + array = numpy.empty(size, dtype) + surface_to_array(array, surface) + return array + +def pixels2d(surface): + """pygame.numpyarray.pixels2d(Surface): return array + + reference pixels into a 2d array + + Create a new 2D array that directly references the pixel values in a + Surface. Any changes to the array will affect the pixels in the + Surface. This is a fast operation since no data is copied. + + Pixels from a 24-bit Surface cannot be referenced, but all other + Surface bit depths can. + + The Surface this references will remain locked for the lifetime of + the array (see the Surface.lock - lock the Surface memory for pixel + access method). + """ + if (surface.get_bitsize() not in _pixel2d_bitdepths): + raise ValueError("unsupport bit depth for 2D reference array") + try: + return numpy_array(surface.get_view('2'), copy=False) + except (ValueError, TypeError): + raise ValueError("bit depth %i unsupported for 2D reference array" % + (surface.get_bitsize(),)) + +def array3d(surface): + """pygame.numpyarray.array3d(Surface): return array + + copy pixels into a 3d array + + Copy the pixels from a Surface into a 3D array. The bit depth of the + surface will control the size of the integer values, and will work + for any type of pixel format. + + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + w, h = surface.get_size() + array = numpy.empty((w, h, 3), numpy.uint8) + surface_to_array(array, surface) + return array + +def pixels3d (surface): + """pygame.numpyarray.pixels3d(Surface): return array + + reference pixels into a 3d array + + Create a new 3D array that directly references the pixel values in a + Surface. Any changes to the array will affect the pixels in the + Surface. This is a fast operation since no data is copied. + + This will only work on Surfaces that have 24-bit or 32-bit + formats. Lower pixel formats cannot be referenced. + + The Surface this references will remain locked for the lifetime of + the array (see the Surface.lock - lock the Surface memory for pixel + access method). + """ + return numpy_array(surface.get_view('3'), copy=False) + +def array_alpha(surface): + """pygame.numpyarray.array_alpha(Surface): return array + + copy pixel alphas into a 2d array + + Copy the pixel alpha values (degree of transparency) from a Surface + into a 2D array. This will work for any type of Surface + format. Surfaces without a pixel alpha will return an array with all + opaque values. + + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + size = surface.get_size() + array = numpy.empty(size, numpy.uint8) + surface_to_array(array, surface, 'A') + return array + +def pixels_alpha(surface): + """pygame.numpyarray.pixels_alpha(Surface): return array + + reference pixel alphas into a 2d array + + Create a new 2D array that directly references the alpha values + (degree of transparency) in a Surface. Any changes to the array will + affect the pixels in the Surface. This is a fast operation since no + data is copied. + + This can only work on 32-bit Surfaces with a per-pixel alpha value. + + The Surface this array references will remain locked for the + lifetime of the array. + """ + return numpy.array(surface.get_view('A'), copy=False) + +def pixels_red(surface): + """pygame.surfarray.pixels_red(Surface): return array + + Reference pixel red into a 2d array. + + Create a new 2D array that directly references the red values + in a Surface. Any changes to the array will affect the pixels + in the Surface. This is a fast operation since no data is copied. + + This can only work on 24-bit or 32-bit Surfaces. + + The Surface this array references will remain locked for the + lifetime of the array. + """ + return numpy.array(surface.get_view('R'), copy=False) + +def array_red(surface): + """pygame.numpyarray.array_red(Surface): return array + + copy pixel red into a 2d array + + Copy the pixel red values from a Surface into a 2D array. This will work + for any type of Surface format. + + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + size = surface.get_size() + array = numpy.empty(size, numpy.uint8) + surface_to_array(array, surface, 'R') + return array + +def pixels_green(surface): + """pygame.surfarray.pixels_green(Surface): return array + + Reference pixel green into a 2d array. + + Create a new 2D array that directly references the green values + in a Surface. Any changes to the array will affect the pixels + in the Surface. This is a fast operation since no data is copied. + + This can only work on 24-bit or 32-bit Surfaces. + + The Surface this array references will remain locked for the + lifetime of the array. + """ + return numpy.array(surface.get_view('G'), copy=False) + +def array_green(surface): + """pygame.numpyarray.array_green(Surface): return array + + copy pixel green into a 2d array + + Copy the pixel green values from a Surface into a 2D array. This will work + for any type of Surface format. + + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + size = surface.get_size() + array = numpy.empty(size, numpy.uint8) + surface_to_array(array, surface, 'G') + return array + +def pixels_blue (surface): + """pygame.surfarray.pixels_blue(Surface): return array + + Reference pixel blue into a 2d array. + + Create a new 2D array that directly references the blue values + in a Surface. Any changes to the array will affect the pixels + in the Surface. This is a fast operation since no data is copied. + + This can only work on 24-bit or 32-bit Surfaces. + + The Surface this array references will remain locked for the + lifetime of the array. + """ + return numpy.array(surface.get_view('B'), copy=False) + +def array_blue(surface): + """pygame.numpyarray.array_blue(Surface): return array + + copy pixel blue into a 2d array + + Copy the pixel blue values from a Surface into a 2D array. This will work + for any type of Surface format. + + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + size = surface.get_size() + array = numpy.empty(size, numpy.uint8) + surface_to_array(array, surface, 'B') + return array + +def array_colorkey(surface): + """pygame.numpyarray.array_colorkey(Surface): return array + + copy the colorkey values into a 2d array + + Create a new array with the colorkey transparency value from each + pixel. If the pixel matches the colorkey it will be fully + tranparent; otherwise it will be fully opaque. + + This will work on any type of Surface format. If the image has no + colorkey a solid opaque array will be returned. + + This function will temporarily lock the Surface as pixels are + copied. + """ + size = surface.get_size() + array = numpy.empty(size, numpy.uint8) + surface_to_array(array, surface, 'C') + return array + +def map_array(surface, array): + """pygame.numpyarray.map_array(Surface, array3d): return array2d + + map a 3d array into a 2d array + + Convert a 3D array into a 2D array. This will use the given Surface + format to control the conversion. + + Note: arrays do not need to be 3D, as long as the minor axis has + three elements giving the component colours, any array shape can be + used (for example, a single colour can be mapped, or an array of + colours). The array shape is limited to eleven dimensions maximum, + including the three element minor axis. + """ + if array.ndim == 0: + raise ValueError("array must have at least 1 dimension") + shape = array.shape + if shape[-1] != 3: + raise ValueError("array must be a 3d array of 3-value color data") + target = numpy_empty(shape[:-1], numpy.int32) + pix_map_array(target, array, surface) + return target + diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/__init__.py new file mode 100644 index 0000000..38b0d92 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/__init__.py @@ -0,0 +1,3 @@ +from .sdl2 import * +from .audio import * +from .video import * diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/__init__.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/__init__.pyi new file mode 100644 index 0000000..74c6eb5 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/__init__.pyi @@ -0,0 +1 @@ +from .touch import * diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/audio.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/audio.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..d4130c8 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/audio.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/controller.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/controller.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..ef1280c Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/controller.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/mixer.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/mixer.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..b7648c9 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/mixer.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/sdl2.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/sdl2.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..0b9b403 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/sdl2.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/touch.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/touch.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..efa7d09 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/touch.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/touch.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/touch.pyi new file mode 100644 index 0000000..93e2745 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/touch.pyi @@ -0,0 +1,6 @@ +from typing import Dict, Union + +def get_num_devices() -> int: ... +def get_device(index: int) -> int: ... +def get_num_fingers(device_id: int) -> int: ... +def get_finger(touchid: int, index: int) -> Dict[str, Union[int, float]]: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/video.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/video.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..d8af7ff Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/_sdl2/video.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/_sprite.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/_sprite.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..a375a48 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/_sprite.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/base.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/base.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..72e6c32 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/base.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/bufferproxy.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/bufferproxy.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..5fbc399 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/bufferproxy.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/bufferproxy.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/bufferproxy.pyi new file mode 100644 index 0000000..25ef48f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/bufferproxy.pyi @@ -0,0 +1,13 @@ +from typing import Any, overload, Optional, TypeVar, Text + +AnyStr = TypeVar("AnyStr", Text, bytes) + +class BufferProxy(object): + parent: Any + length: int + raw: AnyStr + @overload + def __init__(self) -> None: ... + @overload + def __init__(self, parent: Any) -> None: ... + def write(self, buffer: bytes, offset: Optional[int] = 0) -> None: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/camera.py b/Display/.venv/lib/python3.7/site-packages/pygame/camera.py new file mode 100644 index 0000000..ba5b468 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/camera.py @@ -0,0 +1,144 @@ + +_is_init = 0 + + + +def init(): + global list_cameras, Camera, colorspace, _is_init + + + import os,sys + + use_opencv = False + use_vidcapture = False + use__camera = True + + + if sys.platform == 'win32': + use_vidcapture = True + use__camera = False + + elif "linux" in sys.platform: + use__camera = True + elif "darwin" in sys.platform: + use__camera = True + else: + use_opencv = True + + + + # see if we have any user specified defaults in environments. + camera_env = os.environ.get("PYGAME_CAMERA", "") + if camera_env == "opencv": + use_opencv = True + if camera_env == "vidcapture": + use_vidcapture = True + + + + # select the camera module to import here. + + # the _camera module has some code which can be reused by other modules. + # it will also be the default one. + if use__camera: + from pygame import _camera + colorspace = _camera.colorspace + + list_cameras = _camera.list_cameras + Camera = _camera.Camera + + if use_opencv: + try: + from pygame import _camera_opencv_highgui + except ImportError: + _camera_opencv_highgui = None + + if _camera_opencv_highgui: + _camera_opencv_highgui.init() + + list_cameras = _camera_opencv_highgui.list_cameras + Camera = _camera_opencv_highgui.Camera + + if use_vidcapture: + try: + from pygame import _camera_vidcapture + except ImportError: + _camera_vidcapture = None + + if _camera_vidcapture: + _camera_vidcapture.init() + list_cameras = _camera_vidcapture.list_cameras + Camera = _camera_vidcapture.Camera + + + + _is_init = 1 + + +def quit(): + global _is_init + _is_init = 0 + + +def _check_init(): + global _is_init + if not _is_init: + raise ValueError("Need to call camera.init() before using.") + +def list_cameras(): + """ + """ + _check_init() + raise NotImplementedError() + + +class Camera: + + def __init__(self, device =0, size = (320, 200), mode = "RGB"): + """ + """ + _check_init() + raise NotImplementedError() + + def set_resolution(self, width, height): + """Sets the capture resolution. (without dialog) + """ + pass + + def start(self): + """ + """ + + def stop(self): + """ + """ + + def get_buffer(self): + """ + """ + + def set_controls(self, **kwargs): + """ + """ + + def get_image(self, dest_surf = None): + """ + """ + + def get_surface(self, dest_surf = None): + """ + """ + + + +if __name__ == "__main__": + + # try and use this camera stuff with the pygame camera example. + import pygame.examples.camera + + #pygame.camera.Camera = Camera + #pygame.camera.list_cameras = list_cameras + pygame.examples.camera.main() + + + diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/camera.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/camera.pyi new file mode 100644 index 0000000..3ff7d42 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/camera.pyi @@ -0,0 +1,27 @@ +from typing import List, Optional, Union, Tuple +from pygame.surface import Surface + +def init() -> None: ... +def quit() -> None: ... +def list_cameras() -> List[str]: ... + +class Camera: + def __init__( + self, + device: Optional[int] = 0, + size: Optional[Union[Tuple[int, int], List[int]]] = (320, 200), + format: Optional[str] = "RGB", + ): ... + def start(self) -> None: ... + def stop(self) -> None: ... + def get_controls(self) -> Tuple[bool, bool, int]: ... + def set_controls( + self, + hflip: Optional[bool] = ..., + vflip: Optional[bool] = ..., + brightness: Optional[int] = ..., + ) -> Tuple[bool, bool, int]: ... + def get_size(self) -> Tuple[int, int]: ... + def query_image(self) -> bool: ... + def get_image(self, surface: Surface) -> Surface: ... + def get_raw(self) -> str: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/color.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/color.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..e4254ec Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/color.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/color.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/color.pyi new file mode 100644 index 0000000..275b25b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/color.pyi @@ -0,0 +1,21 @@ +from typing import Text, Tuple, Union, overload + +class Color(object): + r: int + g: int + b: int + a: int + cmy: Tuple[float, float, float] + hsva: Tuple[float, float, float, float] + hsla: Tuple[float, float, float, float] + i1i2i3: Tuple[float, float, float] + @overload + def __init__(self, name: Text) -> None: ... + @overload + def __init__(self, r: int, g: int, b: int, a: int = ...) -> None: ... + @overload + def __init__(self, rgbvalue: Union[Text, int]) -> None: ... + def normalize(self) -> Tuple[float, float, float, float]: ... + def correct_gamma(self, gamma: float) -> Color: ... + def set_length(self, len: int) -> None: ... + def lerp(self, color: Color, amount: float) -> Color: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/colordict.py b/Display/.venv/lib/python3.7/site-packages/pygame/colordict.py new file mode 100644 index 0000000..52b1166 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/colordict.py @@ -0,0 +1,684 @@ +## pygame - Python Game Library +## Copyright (C) 2000-2003 Pete Shinners +## +## This library is free software; you can redistribute it and/or +## modify it under the terms of the GNU Library General Public +## License as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## Library General Public License for more details. +## +## You should have received a copy of the GNU Library General Public +## License along with this library; if not, write to the Free +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## +## Pete Shinners +## pete@shinners.org + +from pygame.compat import unicode_ + +THECOLORS = { +'gray17' : (43, 43, 43, 255) , +'gold' : (255, 215, 0, 255) , +'gray10' : (26, 26, 26, 255) , +'yellow' : (255, 255, 0, 255) , +'gray11' : (28, 28, 28, 255) , +'grey61' : (156, 156, 156, 255) , +'grey60' : (153, 153, 153, 255) , +'darkseagreen' : (143, 188, 143, 255) , +'grey62' : (158, 158, 158, 255) , +'grey65' : (166, 166, 166, 255) , +'gray12' : (31, 31, 31, 255) , +'grey67' : (171, 171, 171, 255) , +'grey66' : (168, 168, 168, 255) , +'grey69' : (176, 176, 176, 255) , +'gray21' : (54, 54, 54, 255) , +'lightsalmon4' : (139, 87, 66, 255) , +'lightsalmon2' : (238, 149, 114, 255) , +'lightsalmon3' : (205, 129, 98, 255) , +'lightsalmon1' : (255, 160, 122, 255) , +'gray32' : (82, 82, 82, 255) , +'green4' : (0, 139, 0, 255) , +'gray30' : (77, 77, 77, 255) , +'gray31' : (79, 79, 79, 255) , +'green1' : (0, 255, 0, 255) , +'gray37' : (94, 94, 94, 255) , +'green3' : (0, 205, 0, 255) , +'green2' : (0, 238, 0, 255) , +'darkslategray1' : (151, 255, 255, 255) , +'darkslategray2' : (141, 238, 238, 255) , +'darkslategray3' : (121, 205, 205, 255) , +'aquamarine1' : (127, 255, 212, 255) , +'aquamarine3' : (102, 205, 170, 255) , +'aquamarine2' : (118, 238, 198, 255) , +'papayawhip' : (255, 239, 213, 255) , +'black' : (0, 0, 0, 255) , +'darkorange3' : (205, 102, 0, 255) , +'oldlace' : (253, 245, 230, 255) , +'lightgoldenrod4' : (139, 129, 76, 255) , +'gray90' : (229, 229, 229, 255) , +'orchid1' : (255, 131, 250, 255) , +'orchid2' : (238, 122, 233, 255) , +'orchid3' : (205, 105, 201, 255) , +'grey68' : (173, 173, 173, 255) , +'brown' : (165, 42, 42, 255) , +'purple2' : (145, 44, 238, 255) , +'gray80' : (204, 204, 204, 255) , +'antiquewhite3' : (205, 192, 176, 255) , +'antiquewhite2' : (238, 223, 204, 255) , +'antiquewhite1' : (255, 239, 219, 255) , +'palevioletred3' : (205, 104, 137, 255) , +'hotpink' : (255, 105, 180, 255) , +'lightcyan' : (224, 255, 255, 255) , +'coral3' : (205, 91, 69, 255) , +'gray8' : (20, 20, 20, 255) , +'gray9' : (23, 23, 23, 255) , +'grey32' : (82, 82, 82, 255) , +'bisque4' : (139, 125, 107, 255) , +'cyan' : (0, 255, 255, 255) , +'gray0' : (0, 0, 0, 255) , +'gray1' : (3, 3, 3, 255) , +'gray6' : (15, 15, 15, 255) , +'bisque1' : (255, 228, 196, 255) , +'bisque2' : (238, 213, 183, 255) , +'bisque3' : (205, 183, 158, 255) , +'skyblue' : (135, 206, 235, 255) , +'gray' : (190, 190, 190, 255) , +'darkturquoise' : (0, 206, 209, 255) , +'rosybrown4' : (139, 105, 105, 255) , +'deepskyblue3' : (0, 154, 205, 255) , +'grey63' : (161, 161, 161, 255) , +'indianred1' : (255, 106, 106, 255) , +'grey78' : (199, 199, 199, 255) , +'lightpink' : (255, 182, 193, 255) , +'gray88' : (224, 224, 224, 255) , +'gray22' : (56, 56, 56, 255) , +'red' : (255, 0, 0, 255) , +'grey11' : (28, 28, 28, 255) , +'lemonchiffon3' : (205, 201, 165, 255) , +'lemonchiffon2' : (238, 233, 191, 255) , +'lemonchiffon1' : (255, 250, 205, 255) , +'indianred3' : (205, 85, 85, 255) , +'violetred1' : (255, 62, 150, 255) , +'plum2' : (238, 174, 238, 255) , +'plum1' : (255, 187, 255, 255) , +'lemonchiffon4' : (139, 137, 112, 255) , +'gray99' : (252, 252, 252, 255) , +'grey13' : (33, 33, 33, 255) , +'grey55' : (140, 140, 140, 255) , +'darkcyan' : (0, 139, 139, 255) , +'chocolate4' : (139, 69, 19, 255) , +'lightgoldenrodyellow' : (250, 250, 210, 255) , +'gray54' : (138, 138, 138, 255) , +'lavender' : (230, 230, 250, 255) , +'chartreuse3' : (102, 205, 0, 255) , +'chartreuse2' : (118, 238, 0, 255) , +'chartreuse1' : (127, 255, 0, 255) , +'grey48' : (122, 122, 122, 255) , +'grey16' : (41, 41, 41, 255) , +'thistle' : (216, 191, 216, 255) , +'chartreuse4' : (69, 139, 0, 255) , +'darkorchid4' : (104, 34, 139, 255) , +'grey42' : (107, 107, 107, 255) , +'grey41' : (105, 105, 105, 255) , +'grey17' : (43, 43, 43, 255) , +'dimgrey' : (105, 105, 105, 255) , +'dodgerblue4' : (16, 78, 139, 255) , +'darkorchid2' : (178, 58, 238, 255) , +'darkorchid3' : (154, 50, 205, 255) , +'blue' : (0, 0, 255, 255) , +'rosybrown2' : (238, 180, 180, 255) , +'honeydew' : (240, 255, 240, 255) , +'gray18' : (46, 46, 46, 255) , +'cornflowerblue' : (100, 149, 237, 255) , +'grey91' : (232, 232, 232, 255) , +'gray14' : (36, 36, 36, 255) , +'gray15' : (38, 38, 38, 255) , +'gray16' : (41, 41, 41, 255) , +'maroon4' : (139, 28, 98, 255) , +'maroon3' : (205, 41, 144, 255) , +'maroon2' : (238, 48, 167, 255) , +'maroon1' : (255, 52, 179, 255) , +'gray13' : (33, 33, 33, 255) , +'gold3' : (205, 173, 0, 255) , +'gold2' : (238, 201, 0, 255) , +'gold1' : (255, 215, 0, 255) , +'grey79' : (201, 201, 201, 255) , +'palevioletred1' : (255, 130, 171, 255) , +'palevioletred2' : (238, 121, 159, 255) , +'gold4' : (139, 117, 0, 255) , +'gray41' : (105, 105, 105, 255) , +'gray84' : (214, 214, 214, 255) , +'mediumpurple' : (147, 112, 219, 255) , +'rosybrown1' : (255, 193, 193, 255) , +'lightblue2' : (178, 223, 238, 255) , +'lightblue3' : (154, 192, 205, 255) , +'grey57' : (145, 145, 145, 255) , +'lightblue1' : (191, 239, 255, 255) , +'lightblue4' : (104, 131, 139, 255) , +'gray33' : (84, 84, 84, 255) , +'skyblue4' : (74, 112, 139, 255) , +'grey97' : (247, 247, 247, 255) , +'skyblue1' : (135, 206, 255, 255) , +'gray27' : (69, 69, 69, 255) , +'skyblue3' : (108, 166, 205, 255) , +'skyblue2' : (126, 192, 238, 255) , +'lavenderblush1' : (255, 240, 245, 255) , +'darkgrey' : (169, 169, 169, 255) , +'lavenderblush3' : (205, 193, 197, 255) , +'darkslategrey' : (47, 79, 79, 255) , +'lavenderblush4' : (139, 131, 134, 255) , +'deeppink4' : (139, 10, 80, 255) , +'grey99' : (252, 252, 252, 255) , +'gray36' : (92, 92, 92, 255) , +'coral4' : (139, 62, 47, 255) , +'magenta3' : (205, 0, 205, 255) , +'lightskyblue4' : (96, 123, 139, 255) , +'mediumturquoise' : (72, 209, 204, 255) , +'gray34' : (87, 87, 87, 255) , +'floralwhite' : (255, 250, 240, 255) , +'grey39' : (99, 99, 99, 255) , +'grey36' : (92, 92, 92, 255) , +'grey37' : (94, 94, 94, 255) , +'grey34' : (87, 87, 87, 255) , +'gray26' : (66, 66, 66, 255) , +'royalblue2' : (67, 110, 238, 255) , +'grey33' : (84, 84, 84, 255) , +'turquoise1' : (0, 245, 255, 255) , +'grey31' : (79, 79, 79, 255) , +'steelblue1' : (99, 184, 255, 255) , +'sienna4' : (139, 71, 38, 255) , +'steelblue3' : (79, 148, 205, 255) , +'lavenderblush2' : (238, 224, 229, 255) , +'sienna1' : (255, 130, 71, 255) , +'steelblue4' : (54, 100, 139, 255) , +'sienna3' : (205, 104, 57, 255) , +'aquamarine4' : (69, 139, 116, 255) , +'lightyellow1' : (255, 255, 224, 255) , +'lightyellow2' : (238, 238, 209, 255) , +'lightsteelblue' : (176, 196, 222, 255) , +'lightyellow4' : (139, 139, 122, 255) , +'magenta2' : (238, 0, 238, 255) , +'lightskyblue1' : (176, 226, 255, 255) , +'lightgoldenrod' : (238, 221, 130, 255) , +'magenta4' : (139, 0, 139, 255) , +'gray87' : (222, 222, 222, 255) , +'greenyellow' : (173, 255, 47, 255) , +'navajowhite4' : (139, 121, 94, 255) , +'darkslategray4' : (82, 139, 139, 255) , +'olivedrab' : (107, 142, 35, 255) , +'navajowhite1' : (255, 222, 173, 255) , +'navajowhite2' : (238, 207, 161, 255) , +'darkgoldenrod1' : (255, 185, 15, 255) , +'sienna' : (160, 82, 45, 255) , +'blue1' : (0, 0, 255, 255) , +'yellow1' : (255, 255, 0, 255) , +'gray61' : (156, 156, 156, 255) , +'magenta1' : (255, 0, 255, 255) , +'grey52' : (133, 133, 133, 255) , +'orangered4' : (139, 37, 0, 255) , +'palegreen' : (152, 251, 152, 255) , +'gray86' : (219, 219, 219, 255) , +'grey80' : (204, 204, 204, 255) , +'seashell' : (255, 245, 238, 255) , +'royalblue' : (65, 105, 225, 255) , +'firebrick3' : (205, 38, 38, 255) , +'blue4' : (0, 0, 139, 255) , +'peru' : (205, 133, 63, 255) , +'gray60' : (153, 153, 153, 255) , +'aquamarine' : (127, 255, 212, 255) , +'grey53' : (135, 135, 135, 255) , +'tan4' : (139, 90, 43, 255) , +'darkgoldenrod' : (184, 134, 11, 255) , +'tan2' : (238, 154, 73, 255) , +'tan1' : (255, 165, 79, 255) , +'darkslategray' : (47, 79, 79, 255) , +'royalblue3' : (58, 95, 205, 255) , +'red2' : (238, 0, 0, 255) , +'red1' : (255, 0, 0, 255) , +'dodgerblue' : (30, 144, 255, 255) , +'violetred4' : (139, 34, 82, 255) , +'lightyellow' : (255, 255, 224, 255) , +'paleturquoise1' : (187, 255, 255, 255) , +'firebrick2' : (238, 44, 44, 255) , +'mediumaquamarine' : (102, 205, 170, 255) , +'lemonchiffon' : (255, 250, 205, 255) , +'chocolate' : (210, 105, 30, 255) , +'orchid4' : (139, 71, 137, 255) , +'maroon' : (176, 48, 96, 255) , +'gray38' : (97, 97, 97, 255) , +'darkorange4' : (139, 69, 0, 255) , +'mintcream' : (245, 255, 250, 255) , +'darkorange1' : (255, 127, 0, 255) , +'antiquewhite' : (250, 235, 215, 255) , +'darkorange2' : (238, 118, 0, 255) , +'grey18' : (46, 46, 46, 255) , +'grey19' : (48, 48, 48, 255) , +'grey38' : (97, 97, 97, 255) , +'moccasin' : (255, 228, 181, 255) , +'grey10' : (26, 26, 26, 255) , +'chocolate1' : (255, 127, 36, 255) , +'chocolate2' : (238, 118, 33, 255) , +'chocolate3' : (205, 102, 29, 255) , +'saddlebrown' : (139, 69, 19, 255) , +'grey15' : (38, 38, 38, 255) , +'darkslateblue' : (72, 61, 139, 255) , +'lightskyblue' : (135, 206, 250, 255) , +'gray69' : (176, 176, 176, 255) , +'gray68' : (173, 173, 173, 255) , +'deeppink' : (255, 20, 147, 255) , +'gray65' : (166, 166, 166, 255) , +'gray64' : (163, 163, 163, 255) , +'gray67' : (171, 171, 171, 255) , +'gray66' : (168, 168, 168, 255) , +'gray25' : (64, 64, 64, 255) , +'coral' : (255, 127, 80, 255) , +'gray63' : (161, 161, 161, 255) , +'gray62' : (158, 158, 158, 255) , +'goldenrod4' : (139, 105, 20, 255) , +'grey35' : (89, 89, 89, 255) , +'gray89' : (227, 227, 227, 255) , +'goldenrod1' : (255, 193, 37, 255) , +'goldenrod2' : (238, 180, 34, 255) , +'goldenrod3' : (205, 155, 29, 255) , +'springgreen1' : (0, 255, 127, 255) , +'springgreen2' : (0, 238, 118, 255) , +'springgreen3' : (0, 205, 102, 255) , +'springgreen4' : (0, 139, 69, 255) , +'mistyrose1' : (255, 228, 225, 255) , +'sandybrown' : (244, 164, 96, 255) , +'grey30' : (77, 77, 77, 255) , +'seashell2' : (238, 229, 222, 255) , +'seashell3' : (205, 197, 191, 255) , +'tan' : (210, 180, 140, 255) , +'seashell1' : (255, 245, 238, 255) , +'mistyrose3' : (205, 183, 181, 255) , +'magenta' : (255, 0, 255, 255) , +'pink' : (255, 192, 203, 255) , +'ivory2' : (238, 238, 224, 255) , +'ivory1' : (255, 255, 240, 255) , +'lightcyan2' : (209, 238, 238, 255) , +'mediumseagreen' : (60, 179, 113, 255) , +'ivory4' : (139, 139, 131, 255) , +'darkorange' : (255, 140, 0, 255) , +'powderblue' : (176, 224, 230, 255) , +'dodgerblue1' : (30, 144, 255, 255) , +'gray95' : (242, 242, 242, 255) , +'firebrick1' : (255, 48, 48, 255) , +'gray7' : (18, 18, 18, 255) , +'mistyrose4' : (139, 125, 123, 255) , +'tomato' : (255, 99, 71, 255) , +'indianred2' : (238, 99, 99, 255) , +'steelblue2' : (92, 172, 238, 255) , +'gray100' : (255, 255, 255, 255) , +'seashell4' : (139, 134, 130, 255) , +'grey89' : (227, 227, 227, 255) , +'grey88' : (224, 224, 224, 255) , +'grey87' : (222, 222, 222, 255) , +'grey86' : (219, 219, 219, 255) , +'grey85' : (217, 217, 217, 255) , +'grey84' : (214, 214, 214, 255) , +'midnightblue' : (25, 25, 112, 255) , +'grey82' : (209, 209, 209, 255) , +'grey81' : (207, 207, 207, 255) , +'yellow3' : (205, 205, 0, 255) , +'ivory3' : (205, 205, 193, 255) , +'grey22' : (56, 56, 56, 255) , +'gray85' : (217, 217, 217, 255) , +'violetred3' : (205, 50, 120, 255) , +'dodgerblue2' : (28, 134, 238, 255) , +'gray42' : (107, 107, 107, 255) , +'sienna2' : (238, 121, 66, 255) , +'grey72' : (184, 184, 184, 255) , +'grey73' : (186, 186, 186, 255) , +'grey70' : (179, 179, 179, 255) , +'palevioletred' : (219, 112, 147, 255) , +'lightslategray' : (119, 136, 153, 255) , +'grey77' : (196, 196, 196, 255) , +'grey74' : (189, 189, 189, 255) , +'slategray1' : (198, 226, 255, 255) , +'pink1' : (255, 181, 197, 255) , +'mediumpurple1' : (171, 130, 255, 255) , +'pink3' : (205, 145, 158, 255) , +'antiquewhite4' : (139, 131, 120, 255) , +'lightpink1' : (255, 174, 185, 255) , +'honeydew2' : (224, 238, 224, 255) , +'khaki4' : (139, 134, 78, 255) , +'darkolivegreen4' : (110, 139, 61, 255) , +'gray45' : (115, 115, 115, 255) , +'slategray3' : (159, 182, 205, 255) , +'darkolivegreen1' : (202, 255, 112, 255) , +'khaki1' : (255, 246, 143, 255) , +'khaki2' : (238, 230, 133, 255) , +'khaki3' : (205, 198, 115, 255) , +'lavenderblush' : (255, 240, 245, 255) , +'honeydew4' : (131, 139, 131, 255) , +'salmon3' : (205, 112, 84, 255) , +'salmon2' : (238, 130, 98, 255) , +'gray92' : (235, 235, 235, 255) , +'salmon4' : (139, 76, 57, 255) , +'gray49' : (125, 125, 125, 255) , +'gray48' : (122, 122, 122, 255) , +'linen' : (250, 240, 230, 255) , +'burlywood1' : (255, 211, 155, 255) , +'green' : (0, 255, 0, 255) , +'gray47' : (120, 120, 120, 255) , +'blueviolet' : (138, 43, 226, 255) , +'brown2' : (238, 59, 59, 255) , +'brown3' : (205, 51, 51, 255) , +'peachpuff' : (255, 218, 185, 255) , +'brown4' : (139, 35, 35, 255) , +'firebrick4' : (139, 26, 26, 255) , +'azure1' : (240, 255, 255, 255) , +'azure3' : (193, 205, 205, 255) , +'azure2' : (224, 238, 238, 255) , +'azure4' : (131, 139, 139, 255) , +'tomato4' : (139, 54, 38, 255) , +'orange4' : (139, 90, 0, 255) , +'firebrick' : (178, 34, 34, 255) , +'indianred' : (205, 92, 92, 255) , +'orange1' : (255, 165, 0, 255) , +'orange3' : (205, 133, 0, 255) , +'orange2' : (238, 154, 0, 255) , +'darkolivegreen' : (85, 107, 47, 255) , +'gray2' : (5, 5, 5, 255) , +'slategrey' : (112, 128, 144, 255) , +'gray81' : (207, 207, 207, 255) , +'darkred' : (139, 0, 0, 255) , +'gray3' : (8, 8, 8, 255) , +'lightsteelblue1' : (202, 225, 255, 255) , +'lightsteelblue2' : (188, 210, 238, 255) , +'lightsteelblue3' : (162, 181, 205, 255) , +'lightsteelblue4' : (110, 123, 139, 255) , +'tomato3' : (205, 79, 57, 255) , +'gray43' : (110, 110, 110, 255) , +'darkgoldenrod4' : (139, 101, 8, 255) , +'grey50' : (127, 127, 127, 255) , +'yellow4' : (139, 139, 0, 255) , +'mediumorchid' : (186, 85, 211, 255) , +'yellow2' : (238, 238, 0, 255) , +'darkgoldenrod2' : (238, 173, 14, 255) , +'darkgoldenrod3' : (205, 149, 12, 255) , +'chartreuse' : (127, 255, 0, 255) , +'mediumblue' : (0, 0, 205, 255) , +'gray4' : (10, 10, 10, 255) , +'springgreen' : (0, 255, 127, 255) , +'orange' : (255, 165, 0, 255) , +'gray5' : (13, 13, 13, 255) , +'lightsalmon' : (255, 160, 122, 255) , +'gray19' : (48, 48, 48, 255) , +'turquoise' : (64, 224, 208, 255) , +'lightseagreen' : (32, 178, 170, 255) , +'grey8' : (20, 20, 20, 255) , +'grey9' : (23, 23, 23, 255) , +'grey6' : (15, 15, 15, 255) , +'grey7' : (18, 18, 18, 255) , +'grey4' : (10, 10, 10, 255) , +'grey5' : (13, 13, 13, 255) , +'grey2' : (5, 5, 5, 255) , +'grey3' : (8, 8, 8, 255) , +'grey0' : (0, 0, 0, 255) , +'grey1' : (3, 3, 3, 255) , +'gray50' : (127, 127, 127, 255) , +'goldenrod' : (218, 165, 32, 255) , +'grey58' : (148, 148, 148, 255) , +'grey59' : (150, 150, 150, 255) , +'gray51' : (130, 130, 130, 255) , +'grey54' : (138, 138, 138, 255) , +'mediumorchid4' : (122, 55, 139, 255) , +'grey56' : (143, 143, 143, 255) , +'navajowhite3' : (205, 179, 139, 255) , +'mediumorchid1' : (224, 102, 255, 255) , +'grey51' : (130, 130, 130, 255) , +'mediumorchid3' : (180, 82, 205, 255) , +'mediumorchid2' : (209, 95, 238, 255) , +'cyan2' : (0, 238, 238, 255) , +'cyan3' : (0, 205, 205, 255) , +'gray23' : (59, 59, 59, 255) , +'cyan1' : (0, 255, 255, 255) , +'darkgreen' : (0, 100, 0, 255) , +'gray24' : (61, 61, 61, 255) , +'cyan4' : (0, 139, 139, 255) , +'darkviolet' : (148, 0, 211, 255) , +'peachpuff4' : (139, 119, 101, 255) , +'gray28' : (71, 71, 71, 255) , +'slateblue4' : (71, 60, 139, 255) , +'slateblue3' : (105, 89, 205, 255) , +'peachpuff1' : (255, 218, 185, 255) , +'peachpuff2' : (238, 203, 173, 255) , +'peachpuff3' : (205, 175, 149, 255) , +'gray29' : (74, 74, 74, 255) , +'paleturquoise' : (175, 238, 238, 255) , +'darkgray' : (169, 169, 169, 255) , +'grey25' : (64, 64, 64, 255) , +'darkmagenta' : (139, 0, 139, 255) , +'palegoldenrod' : (238, 232, 170, 255) , +'grey64' : (163, 163, 163, 255) , +'grey12' : (31, 31, 31, 255) , +'deeppink3' : (205, 16, 118, 255) , +'gray79' : (201, 201, 201, 255) , +'gray83' : (212, 212, 212, 255) , +'deeppink2' : (238, 18, 137, 255) , +'burlywood4' : (139, 115, 85, 255) , +'palevioletred4' : (139, 71, 93, 255) , +'deeppink1' : (255, 20, 147, 255) , +'slateblue2' : (122, 103, 238, 255) , +'grey46' : (117, 117, 117, 255) , +'royalblue4' : (39, 64, 139, 255) , +'yellowgreen' : (154, 205, 50, 255) , +'royalblue1' : (72, 118, 255, 255) , +'slateblue1' : (131, 111, 255, 255) , +'lightgoldenrod3' : (205, 190, 112, 255) , +'lightgoldenrod2' : (238, 220, 130, 255) , +'navy' : (0, 0, 128, 255) , +'orchid' : (218, 112, 214, 255) , +'ghostwhite' : (248, 248, 255, 255) , +'purple' : (160, 32, 240, 255) , +'darkkhaki' : (189, 183, 107, 255) , +'grey45' : (115, 115, 115, 255) , +'gray94' : (240, 240, 240, 255) , +'wheat4' : (139, 126, 102, 255) , +'gray96' : (245, 245, 245, 255) , +'gray97' : (247, 247, 247, 255) , +'wheat1' : (255, 231, 186, 255) , +'gray91' : (232, 232, 232, 255) , +'wheat3' : (205, 186, 150, 255) , +'wheat2' : (238, 216, 174, 255) , +'indianred4' : (139, 58, 58, 255) , +'coral2' : (238, 106, 80, 255) , +'coral1' : (255, 114, 86, 255) , +'violetred' : (208, 32, 144, 255) , +'rosybrown3' : (205, 155, 155, 255) , +'deepskyblue2' : (0, 178, 238, 255) , +'deepskyblue1' : (0, 191, 255, 255) , +'bisque' : (255, 228, 196, 255) , +'grey49' : (125, 125, 125, 255) , +'khaki' : (240, 230, 140, 255) , +'wheat' : (245, 222, 179, 255) , +'lightslateblue' : (132, 112, 255, 255) , +'mediumpurple3' : (137, 104, 205, 255) , +'gray55' : (140, 140, 140, 255) , +'deepskyblue' : (0, 191, 255, 255) , +'gray98' : (250, 250, 250, 255) , +'steelblue' : (70, 130, 180, 255) , +'aliceblue' : (240, 248, 255, 255) , +'lightskyblue2' : (164, 211, 238, 255) , +'lightskyblue3' : (141, 182, 205, 255) , +'lightslategrey' : (119, 136, 153, 255) , +'blue3' : (0, 0, 205, 255) , +'blue2' : (0, 0, 238, 255) , +'gainsboro' : (220, 220, 220, 255) , +'grey76' : (194, 194, 194, 255) , +'purple3' : (125, 38, 205, 255) , +'plum4' : (139, 102, 139, 255) , +'gray56' : (143, 143, 143, 255) , +'plum3' : (205, 150, 205, 255) , +'plum' : (221, 160, 221, 255) , +'lightgrey' : (211, 211, 211, 255) , +'mediumslateblue' : (123, 104, 238, 255) , +'mistyrose' : (255, 228, 225, 255) , +'lightcyan1' : (224, 255, 255, 255) , +'grey71' : (181, 181, 181, 255) , +'darksalmon' : (233, 150, 122, 255) , +'beige' : (245, 245, 220, 255) , +'grey24' : (61, 61, 61, 255) , +'azure' : (240, 255, 255, 255) , +'honeydew1' : (240, 255, 240, 255) , +'slategray2' : (185, 211, 238, 255) , +'dodgerblue3' : (24, 116, 205, 255) , +'slategray4' : (108, 123, 139, 255) , +'grey27' : (69, 69, 69, 255) , +'lightcyan3' : (180, 205, 205, 255) , +'cornsilk' : (255, 248, 220, 255) , +'tomato1' : (255, 99, 71, 255) , +'gray57' : (145, 145, 145, 255) , +'mediumvioletred' : (199, 21, 133, 255) , +'tomato2' : (238, 92, 66, 255) , +'snow4' : (139, 137, 137, 255) , +'grey75' : (191, 191, 191, 255) , +'snow2' : (238, 233, 233, 255) , +'snow3' : (205, 201, 201, 255) , +'snow1' : (255, 250, 250, 255) , +'grey23' : (59, 59, 59, 255) , +'cornsilk3' : (205, 200, 177, 255) , +'lightcoral' : (240, 128, 128, 255) , +'orangered' : (255, 69, 0, 255) , +'navajowhite' : (255, 222, 173, 255) , +'mediumpurple2' : (159, 121, 238, 255) , +'slategray' : (112, 128, 144, 255) , +'pink2' : (238, 169, 184, 255) , +'grey29' : (74, 74, 74, 255) , +'grey28' : (71, 71, 71, 255) , +'gray82' : (209, 209, 209, 255) , +'burlywood' : (222, 184, 135, 255) , +'mediumpurple4' : (93, 71, 139, 255) , +'mediumspringgreen' : (0, 250, 154, 255) , +'grey26' : (66, 66, 66, 255) , +'grey21' : (54, 54, 54, 255) , +'grey20' : (51, 51, 51, 255) , +'blanchedalmond' : (255, 235, 205, 255) , +'pink4' : (139, 99, 108, 255) , +'gray78' : (199, 199, 199, 255) , +'tan3' : (205, 133, 63, 255) , +'gray76' : (194, 194, 194, 255) , +'gray77' : (196, 196, 196, 255) , +'white' : (255, 255, 255, 255) , +'gray75' : (191, 191, 191, 255) , +'gray72' : (184, 184, 184, 255) , +'gray73' : (186, 186, 186, 255) , +'gray70' : (179, 179, 179, 255) , +'gray71' : (181, 181, 181, 255) , +'lightgray' : (211, 211, 211, 255) , +'ivory' : (255, 255, 240, 255) , +'gray46' : (117, 117, 117, 255) , +'gray74' : (189, 189, 189, 255) , +'lightyellow3' : (205, 205, 180, 255) , +'lightpink2' : (238, 162, 173, 255) , +'lightpink3' : (205, 140, 149, 255) , +'paleturquoise4' : (102, 139, 139, 255) , +'lightpink4' : (139, 95, 101, 255) , +'paleturquoise3' : (150, 205, 205, 255) , +'seagreen4' : (46, 139, 87, 255) , +'seagreen3' : (67, 205, 128, 255) , +'seagreen2' : (78, 238, 148, 255) , +'seagreen1' : (84, 255, 159, 255) , +'paleturquoise2' : (174, 238, 238, 255) , +'gray52' : (133, 133, 133, 255) , +'cornsilk4' : (139, 136, 120, 255) , +'cornsilk2' : (238, 232, 205, 255) , +'darkolivegreen3' : (162, 205, 90, 255) , +'cornsilk1' : (255, 248, 220, 255) , +'limegreen' : (50, 205, 50, 255) , +'darkolivegreen2' : (188, 238, 104, 255) , +'grey' : (190, 190, 190, 255) , +'violetred2' : (238, 58, 140, 255) , +'salmon1' : (255, 140, 105, 255) , +'grey92' : (235, 235, 235, 255) , +'grey93' : (237, 237, 237, 255) , +'grey94' : (240, 240, 240, 255) , +'grey95' : (242, 242, 242, 255) , +'grey96' : (245, 245, 245, 255) , +'grey83' : (212, 212, 212, 255) , +'grey98' : (250, 250, 250, 255) , +'lightgoldenrod1' : (255, 236, 139, 255) , +'palegreen1' : (154, 255, 154, 255) , +'red3' : (205, 0, 0, 255) , +'palegreen3' : (124, 205, 124, 255) , +'palegreen2' : (144, 238, 144, 255) , +'palegreen4' : (84, 139, 84, 255) , +'cadetblue' : (95, 158, 160, 255) , +'violet' : (238, 130, 238, 255) , +'mistyrose2' : (238, 213, 210, 255) , +'slateblue' : (106, 90, 205, 255) , +'grey43' : (110, 110, 110, 255) , +'grey90' : (229, 229, 229, 255) , +'gray35' : (89, 89, 89, 255) , +'turquoise3' : (0, 197, 205, 255) , +'turquoise2' : (0, 229, 238, 255) , +'burlywood3' : (205, 170, 125, 255) , +'burlywood2' : (238, 197, 145, 255) , +'lightcyan4' : (122, 139, 139, 255) , +'rosybrown' : (188, 143, 143, 255) , +'turquoise4' : (0, 134, 139, 255) , +'whitesmoke' : (245, 245, 245, 255) , +'lightblue' : (173, 216, 230, 255) , +'grey40' : (102, 102, 102, 255) , +'gray40' : (102, 102, 102, 255) , +'honeydew3' : (193, 205, 193, 255) , +'dimgray' : (105, 105, 105, 255) , +'grey47' : (120, 120, 120, 255) , +'seagreen' : (46, 139, 87, 255) , +'red4' : (139, 0, 0, 255) , +'grey14' : (36, 36, 36, 255) , +'snow' : (255, 250, 250, 255) , +'darkorchid1' : (191, 62, 255, 255) , +'gray58' : (148, 148, 148, 255) , +'gray59' : (150, 150, 150, 255) , +'cadetblue4' : (83, 134, 139, 255) , +'cadetblue3' : (122, 197, 205, 255) , +'cadetblue2' : (142, 229, 238, 255) , +'cadetblue1' : (152, 245, 255, 255) , +'olivedrab4' : (105, 139, 34, 255) , +'purple4' : (85, 26, 139, 255) , +'gray20' : (51, 51, 51, 255) , +'grey44' : (112, 112, 112, 255) , +'purple1' : (155, 48, 255, 255) , +'olivedrab1' : (192, 255, 62, 255) , +'olivedrab2' : (179, 238, 58, 255) , +'olivedrab3' : (154, 205, 50, 255) , +'orangered3' : (205, 55, 0, 255) , +'orangered2' : (238, 64, 0, 255) , +'orangered1' : (255, 69, 0, 255) , +'darkorchid' : (153, 50, 204, 255) , +'thistle3' : (205, 181, 205, 255) , +'thistle2' : (238, 210, 238, 255) , +'thistle1' : (255, 225, 255, 255) , +'salmon' : (250, 128, 114, 255) , +'gray93' : (237, 237, 237, 255) , +'thistle4' : (139, 123, 139, 255) , +'gray39' : (99, 99, 99, 255) , +'lawngreen' : (124, 252, 0, 255) , +'hotpink3' : (205, 96, 144, 255) , +'hotpink2' : (238, 106, 167, 255) , +'hotpink1' : (255, 110, 180, 255) , +'lightgreen' : (144, 238, 144, 255) , +'hotpink4' : (139, 58, 98, 255) , +'darkseagreen4' : (105, 139, 105, 255) , +'darkseagreen3' : (155, 205, 155, 255) , +'darkseagreen2' : (180, 238, 180, 255) , +'darkseagreen1' : (193, 255, 193, 255) , +'deepskyblue4' : (0, 104, 139, 255) , +'gray44' : (112, 112, 112, 255) , +'navyblue' : (0, 0, 128, 255) , +'darkblue' : (0, 0, 139, 255) , +'forestgreen' : (34, 139, 34, 255) , +'gray53' : (135, 135, 135, 255) , +'grey100' : (255, 255, 255, 255) , +'brown1' : (255, 64, 64, 255) , +} + +for k,v in THECOLORS.items(): + THECOLORS[unicode_(k)] = v diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/compat.py b/Display/.venv/lib/python3.7/site-packages/pygame/compat.py new file mode 100644 index 0000000..58e5c54 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/compat.py @@ -0,0 +1,103 @@ +# coding: ascii +"""Python 2.x/3.x compatibility tools""" + +import sys + +__all__ = ['geterror', 'long_', 'xrange_', 'ord_', 'unichr_', + 'unicode_', 'raw_input_', 'as_bytes', 'as_unicode', + 'bytes_', 'imap_', 'PY_MAJOR_VERSION'] + +PY_MAJOR_VERSION = sys.version_info[0] + + +def geterror(): + return sys.exc_info()[1] + +# Python 3 +if PY_MAJOR_VERSION >= 3: + long_ = int + xrange_ = range + from io import StringIO + from io import BytesIO + unichr_ = chr + unicode_ = str + bytes_ = bytes + raw_input_ = input + imap_ = map + + # Represent escaped bytes and strings in a portable way. + # + # as_bytes: Allow a Python 3.x string to represent a bytes object. + # e.g.: as_bytes("a\x01\b") == b"a\x01b" # Python 3.x + # as_bytes("a\x01\b") == "a\x01b" # Python 2.x + # as_unicode: Allow a Python "r" string to represent a unicode string. + # e.g.: as_unicode(r"Bo\u00F6tes") == u"Bo\u00F6tes" # Python 2.x + # as_unicode(r"Bo\u00F6tes") == "Bo\u00F6tes" # Python 3.x + def as_bytes(string): + """ '<binary literal>' => b'<binary literal>' """ + return string.encode('latin-1', 'strict') + + def as_unicode(rstring): + """ r'<Unicode literal>' => '<Unicode literal>' """ + return rstring.encode('ascii', 'strict').decode('unicode_escape', + 'strict') + +# Python 2 +else: + long_ = long + xrange_ = xrange + from cStringIO import StringIO + BytesIO = StringIO + unichr_ = unichr + unicode_ = unicode + bytes_ = str + raw_input_ = raw_input + from itertools import imap as imap_ + + # Represent escaped bytes and strings in a portable way. + # + # as_bytes: Allow a Python 3.x string to represent a bytes object. + # e.g.: as_bytes("a\x01\b") == b"a\x01b" # Python 3.x + # as_bytes("a\x01\b") == "a\x01b" # Python 2.x + # as_unicode: Allow a Python "r" string to represent a unicode string. + # e.g.: as_unicode(r"Bo\u00F6tes") == u"Bo\u00F6tes" # Python 2.x + # as_unicode(r"Bo\u00F6tes") == "Bo\u00F6tes" # Python 3.x + def as_bytes(string): + """ '<binary literal>' => '<binary literal>' """ + return string + + def as_unicode(rstring): + """ r'<Unicode literal>' => u'<Unicode literal>' """ + return rstring.decode('unicode_escape', 'strict') + + +def get_BytesIO(): + return BytesIO + + +def get_StringIO(): + return StringIO + + +def ord_(o): + try: + return ord(o) + except TypeError: + return o + +if sys.platform == 'win32': + filesystem_errors = "replace" +elif PY_MAJOR_VERSION >= 3: + filesystem_errors = "surrogateescape" +else: + filesystem_errors = "strict" + + +def filesystem_encode(u): + fsencoding = sys.getfilesystemencoding() + if fsencoding.lower() in ['ascii', 'ansi_x3.4-1968'] and sys.platform.startswith('linux'): + # Don't believe Linux systems claiming ASCII-only filesystems. In + # practice, arbitrary bytes are allowed, and most things expect UTF-8. + fsencoding = 'utf-8' + return u.encode(fsencoding, filesystem_errors) + diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/constants.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/constants.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..bf9b396 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/constants.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/constants.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/constants.pyi new file mode 100644 index 0000000..d4db33c --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/constants.pyi @@ -0,0 +1,521 @@ +""" +Script used to generate this file (if we change something in the constants in the future): +import pygame.constants +const = [] +for element in dir(pygame.constants): + constant_type = getattr(pygame.constants, element) + if not element.startswith("_"): + const.append("{}: {}\n".format(element, constant_type.__class__.__name__)) +with open("constants.pyi", "w") as f: + for line in const: + f.write(str(line)) +""" + +ACTIVEEVENT: int +ANYFORMAT: int +APPACTIVE: int +APPFOCUSMOUSE: int +APPINPUTFOCUS: int +ASYNCBLIT: int +AUDIODEVICEADDED: int +AUDIODEVICEREMOVED: int +AUDIO_ALLOW_ANY_CHANGE: int +AUDIO_ALLOW_CHANNELS_CHANGE: int +AUDIO_ALLOW_FORMAT_CHANGE: int +AUDIO_ALLOW_FREQUENCY_CHANGE: int +AUDIO_S16: int +AUDIO_S16LSB: int +AUDIO_S16MSB: int +AUDIO_S16SYS: int +AUDIO_S8: int +AUDIO_U16: int +AUDIO_U16LSB: int +AUDIO_U16MSB: int +AUDIO_U16SYS: int +AUDIO_U8: int +BIG_ENDIAN: int +BLENDMODE_ADD: int +BLENDMODE_BLEND: int +BLENDMODE_MOD: int +BLENDMODE_NONE: int +BLEND_ADD: int +BLEND_MAX: int +BLEND_MIN: int +BLEND_MULT: int +BLEND_PREMULTIPLIED: int +BLEND_RGBA_ADD: int +BLEND_RGBA_MAX: int +BLEND_RGBA_MIN: int +BLEND_RGBA_MULT: int +BLEND_RGBA_SUB: int +BLEND_RGB_ADD: int +BLEND_RGB_MAX: int +BLEND_RGB_MIN: int +BLEND_RGB_MULT: int +BLEND_RGB_SUB: int +BLEND_SUB: int +BUTTON_LEFT: int +BUTTON_MIDDLE: int +BUTTON_RIGHT: int +BUTTON_WHEELDOWN: int +BUTTON_WHEELUP: int +BUTTON_X1: int +BUTTON_X2: int +CONTROLLERAXISMOTION: int +CONTROLLERBUTTONDOWN: int +CONTROLLERBUTTONUP: int +CONTROLLERDEVICEADDED: int +CONTROLLERDEVICEREMAPPED: int +CONTROLLERDEVICEREMOVED: int +CONTROLLER_AXIS_INVALID: int +CONTROLLER_AXIS_LEFTX: int +CONTROLLER_AXIS_LEFTY: int +CONTROLLER_AXIS_MAX: int +CONTROLLER_AXIS_RIGHTX: int +CONTROLLER_AXIS_RIGHTY: int +CONTROLLER_AXIS_TRIGGERLEFT: int +CONTROLLER_AXIS_TRIGGERRIGHT: int +CONTROLLER_BUTTON_A: int +CONTROLLER_BUTTON_B: int +CONTROLLER_BUTTON_BACK: int +CONTROLLER_BUTTON_DPAD_DOWN: int +CONTROLLER_BUTTON_DPAD_LEFT: int +CONTROLLER_BUTTON_DPAD_RIGHT: int +CONTROLLER_BUTTON_DPAD_UP: int +CONTROLLER_BUTTON_GUIDE: int +CONTROLLER_BUTTON_INVALID: int +CONTROLLER_BUTTON_LEFTSHOULDER: int +CONTROLLER_BUTTON_LEFTSTICK: int +CONTROLLER_BUTTON_MAX: int +CONTROLLER_BUTTON_RIGHTSHOULDER: int +CONTROLLER_BUTTON_RIGHTSTICK: int +CONTROLLER_BUTTON_START: int +CONTROLLER_BUTTON_X: int +CONTROLLER_BUTTON_Y: int +DOUBLEBUF: int +DROPBEGIN: int +DROPCOMPLETE: int +DROPFILE: int +DROPTEXT: int +FINGERDOWN: int +FINGERMOTION: int +FINGERUP: int +FULLSCREEN: int +GL_ACCELERATED_VISUAL: int +GL_ACCUM_ALPHA_SIZE: int +GL_ACCUM_BLUE_SIZE: int +GL_ACCUM_GREEN_SIZE: int +GL_ACCUM_RED_SIZE: int +GL_ALPHA_SIZE: int +GL_BLUE_SIZE: int +GL_BUFFER_SIZE: int +GL_CONTEXT_DEBUG_FLAG: int +GL_CONTEXT_FLAGS: int +GL_CONTEXT_FORWARD_COMPATIBLE_FLAG: int +GL_CONTEXT_MAJOR_VERSION: int +GL_CONTEXT_MINOR_VERSION: int +GL_CONTEXT_PROFILE_COMPATIBILITY: int +GL_CONTEXT_PROFILE_CORE: int +GL_CONTEXT_PROFILE_ES: int +GL_CONTEXT_PROFILE_MASK: int +GL_CONTEXT_RELEASE_BEHAVIOR: int +GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH: int +GL_CONTEXT_RELEASE_BEHAVIOR_NONE: int +GL_CONTEXT_RESET_ISOLATION_FLAG: int +GL_CONTEXT_ROBUST_ACCESS_FLAG: int +GL_DEPTH_SIZE: int +GL_DOUBLEBUFFER: int +GL_FRAMEBUFFER_SRGB_CAPABLE: int +GL_GREEN_SIZE: int +GL_MULTISAMPLEBUFFERS: int +GL_MULTISAMPLESAMPLES: int +GL_RED_SIZE: int +GL_SHARE_WITH_CURRENT_CONTEXT: int +GL_STENCIL_SIZE: int +GL_STEREO: int +GL_SWAP_CONTROL: int +HAT_CENTERED: int +HAT_DOWN: int +HAT_LEFT: int +HAT_LEFTDOWN: int +HAT_LEFTUP: int +HAT_RIGHT: int +HAT_RIGHTDOWN: int +HAT_RIGHTUP: int +HAT_UP: int +HIDDEN: int +HWACCEL: int +HWPALETTE: int +HWSURFACE: int +JOYAXISMOTION: int +JOYBALLMOTION: int +JOYBUTTONDOWN: int +JOYBUTTONUP: int +JOYHATMOTION: int +KEYDOWN: int +KEYUP: int +KMOD_ALT: int +KMOD_CAPS: int +KMOD_CTRL: int +KMOD_GUI: int +KMOD_LALT: int +KMOD_LCTRL: int +KMOD_LGUI: int +KMOD_LMETA: int +KMOD_LSHIFT: int +KMOD_META: int +KMOD_MODE: int +KMOD_NONE: int +KMOD_NUM: int +KMOD_RALT: int +KMOD_RCTRL: int +KMOD_RGUI: int +KMOD_RMETA: int +KMOD_RSHIFT: int +KMOD_SHIFT: int +KSCAN_0: int +KSCAN_1: int +KSCAN_2: int +KSCAN_3: int +KSCAN_4: int +KSCAN_5: int +KSCAN_6: int +KSCAN_7: int +KSCAN_8: int +KSCAN_9: int +KSCAN_A: int +KSCAN_APOSTROPHE: int +KSCAN_B: int +KSCAN_BACKSLASH: int +KSCAN_BACKSPACE: int +KSCAN_BREAK: int +KSCAN_C: int +KSCAN_CAPSLOCK: int +KSCAN_CLEAR: int +KSCAN_COMMA: int +KSCAN_CURRENCYSUBUNIT: int +KSCAN_CURRENCYUNIT: int +KSCAN_D: int +KSCAN_DELETE: int +KSCAN_DOWN: int +KSCAN_E: int +KSCAN_END: int +KSCAN_EQUALS: int +KSCAN_ESCAPE: int +KSCAN_EURO: int +KSCAN_F: int +KSCAN_F1: int +KSCAN_F10: int +KSCAN_F11: int +KSCAN_F12: int +KSCAN_F13: int +KSCAN_F14: int +KSCAN_F15: int +KSCAN_F2: int +KSCAN_F3: int +KSCAN_F4: int +KSCAN_F5: int +KSCAN_F6: int +KSCAN_F7: int +KSCAN_F8: int +KSCAN_F9: int +KSCAN_G: int +KSCAN_GRAVE: int +KSCAN_H: int +KSCAN_HELP: int +KSCAN_HOME: int +KSCAN_I: int +KSCAN_INSERT: int +KSCAN_INTERNATIONAL1: int +KSCAN_INTERNATIONAL2: int +KSCAN_INTERNATIONAL3: int +KSCAN_INTERNATIONAL4: int +KSCAN_INTERNATIONAL5: int +KSCAN_INTERNATIONAL6: int +KSCAN_INTERNATIONAL7: int +KSCAN_INTERNATIONAL8: int +KSCAN_INTERNATIONAL9: int +KSCAN_J: int +KSCAN_K: int +KSCAN_KP0: int +KSCAN_KP1: int +KSCAN_KP2: int +KSCAN_KP3: int +KSCAN_KP4: int +KSCAN_KP5: int +KSCAN_KP6: int +KSCAN_KP7: int +KSCAN_KP8: int +KSCAN_KP9: int +KSCAN_KP_0: int +KSCAN_KP_1: int +KSCAN_KP_2: int +KSCAN_KP_3: int +KSCAN_KP_4: int +KSCAN_KP_5: int +KSCAN_KP_6: int +KSCAN_KP_7: int +KSCAN_KP_8: int +KSCAN_KP_9: int +KSCAN_KP_DIVIDE: int +KSCAN_KP_ENTER: int +KSCAN_KP_EQUALS: int +KSCAN_KP_MINUS: int +KSCAN_KP_MULTIPLY: int +KSCAN_KP_PERIOD: int +KSCAN_KP_PLUS: int +KSCAN_L: int +KSCAN_LALT: int +KSCAN_LANG1: int +KSCAN_LANG2: int +KSCAN_LANG3: int +KSCAN_LANG4: int +KSCAN_LANG5: int +KSCAN_LANG6: int +KSCAN_LANG7: int +KSCAN_LANG8: int +KSCAN_LANG9: int +KSCAN_LCTRL: int +KSCAN_LEFT: int +KSCAN_LEFTBRACKET: int +KSCAN_LGUI: int +KSCAN_LMETA: int +KSCAN_LSHIFT: int +KSCAN_LSUPER: int +KSCAN_M: int +KSCAN_MENU: int +KSCAN_MINUS: int +KSCAN_MODE: int +KSCAN_N: int +KSCAN_NONUSBACKSLASH: int +KSCAN_NONUSHASH: int +KSCAN_NUMLOCK: int +KSCAN_NUMLOCKCLEAR: int +KSCAN_O: int +KSCAN_P: int +KSCAN_PAGEDOWN: int +KSCAN_PAGEUP: int +KSCAN_PAUSE: int +KSCAN_PERIOD: int +KSCAN_POWER: int +KSCAN_PRINT: int +KSCAN_PRINTSCREEN: int +KSCAN_Q: int +KSCAN_R: int +KSCAN_RALT: int +KSCAN_RCTRL: int +KSCAN_RETURN: int +KSCAN_RGUI: int +KSCAN_RIGHT: int +KSCAN_RIGHTBRACKET: int +KSCAN_RMETA: int +KSCAN_RSHIFT: int +KSCAN_RSUPER: int +KSCAN_S: int +KSCAN_SCROLLLOCK: int +KSCAN_SCROLLOCK: int +KSCAN_SEMICOLON: int +KSCAN_SLASH: int +KSCAN_SPACE: int +KSCAN_SYSREQ: int +KSCAN_T: int +KSCAN_TAB: int +KSCAN_U: int +KSCAN_UNKNOWN: int +KSCAN_UP: int +KSCAN_V: int +KSCAN_W: int +KSCAN_X: int +KSCAN_Y: int +KSCAN_Z: int +K_0: int +K_1: int +K_2: int +K_3: int +K_4: int +K_5: int +K_6: int +K_7: int +K_8: int +K_9: int +K_AMPERSAND: int +K_ASTERISK: int +K_AT: int +K_BACKQUOTE: int +K_BACKSLASH: int +K_BACKSPACE: int +K_BREAK: int +K_CAPSLOCK: int +K_CARET: int +K_CLEAR: int +K_COLON: int +K_COMMA: int +K_CURRENCYSUBUNIT: int +K_CURRENCYUNIT: int +K_DELETE: int +K_DOLLAR: int +K_DOWN: int +K_END: int +K_EQUALS: int +K_ESCAPE: int +K_EURO: int +K_EXCLAIM: int +K_F1: int +K_F10: int +K_F11: int +K_F12: int +K_F13: int +K_F14: int +K_F15: int +K_F2: int +K_F3: int +K_F4: int +K_F5: int +K_F6: int +K_F7: int +K_F8: int +K_F9: int +K_GREATER: int +K_HASH: int +K_HELP: int +K_HOME: int +K_INSERT: int +K_KP0: int +K_KP1: int +K_KP2: int +K_KP3: int +K_KP4: int +K_KP5: int +K_KP6: int +K_KP7: int +K_KP8: int +K_KP9: int +K_KP_0: int +K_KP_1: int +K_KP_2: int +K_KP_3: int +K_KP_4: int +K_KP_5: int +K_KP_6: int +K_KP_7: int +K_KP_8: int +K_KP_9: int +K_KP_DIVIDE: int +K_KP_ENTER: int +K_KP_EQUALS: int +K_KP_MINUS: int +K_KP_MULTIPLY: int +K_KP_PERIOD: int +K_KP_PLUS: int +K_LALT: int +K_LCTRL: int +K_LEFT: int +K_LEFTBRACKET: int +K_LEFTPAREN: int +K_LESS: int +K_LGUI: int +K_LMETA: int +K_LSHIFT: int +K_LSUPER: int +K_MENU: int +K_MINUS: int +K_MODE: int +K_NUMLOCK: int +K_NUMLOCKCLEAR: int +K_PAGEDOWN: int +K_PAGEUP: int +K_PAUSE: int +K_PERCENT: int +K_PERIOD: int +K_PLUS: int +K_POWER: int +K_PRINT: int +K_PRINTSCREEN: int +K_QUESTION: int +K_QUOTE: int +K_QUOTEDBL: int +K_RALT: int +K_RCTRL: int +K_RETURN: int +K_RGUI: int +K_RIGHT: int +K_RIGHTBRACKET: int +K_RIGHTPAREN: int +K_RMETA: int +K_RSHIFT: int +K_RSUPER: int +K_SCROLLLOCK: int +K_SCROLLOCK: int +K_SEMICOLON: int +K_SLASH: int +K_SPACE: int +K_SYSREQ: int +K_TAB: int +K_UNDERSCORE: int +K_UNKNOWN: int +K_UP: int +K_a: int +K_b: int +K_c: int +K_d: int +K_e: int +K_f: int +K_g: int +K_h: int +K_i: int +K_j: int +K_k: int +K_l: int +K_m: int +K_n: int +K_o: int +K_p: int +K_q: int +K_r: int +K_s: int +K_t: int +K_u: int +K_v: int +K_w: int +K_x: int +K_y: int +K_z: int +LIL_ENDIAN: int +MIDIIN: int +MIDIOUT: int +MOUSEBUTTONDOWN: int +MOUSEBUTTONUP: int +MOUSEMOTION: int +MOUSEWHEEL: int +MULTIGESTURE: int +NOEVENT: int +NOFRAME: int +NUMEVENTS: int +OPENGL: int +OPENGLBLIT: int +PREALLOC: int +QUIT: int +RESIZABLE: int +RLEACCEL: int +RLEACCELOK: int +SCALED: int +SCRAP_BMP: str +SCRAP_CLIPBOARD: int +SCRAP_PBM: str +SCRAP_PPM: str +SCRAP_SELECTION: int +SCRAP_TEXT: str +SHOWN: int +SRCALPHA: int +SRCCOLORKEY: int +SWSURFACE: int +SYSWMEVENT: int +TEXTEDITING: int +TEXTINPUT: int +TIMER_RESOLUTION: int +USEREVENT: int +USEREVENT_DROPFILE: int +VIDEOEXPOSE: int +VIDEORESIZE: int +WINDOWEVENT: int +WINDOWEVENT_CLOSE: int diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/cursors.py b/Display/.venv/lib/python3.7/site-packages/pygame/cursors.py new file mode 100644 index 0000000..47f95d3 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/cursors.py @@ -0,0 +1,310 @@ +## pygame - Python Game Library +## Copyright (C) 2000-2003 Pete Shinners +## +## This library is free software; you can redistribute it and/or +## modify it under the terms of the GNU Library General Public +## License as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## Library General Public License for more details. +## +## You should have received a copy of the GNU Library General Public +## License along with this library; if not, write to the Free +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## +## Pete Shinners +## pete@shinners.org + +"""Set of cursor resources available for use. These cursors come +in a sequence of values that are needed as the arguments for +pygame.mouse.set_cursor(). to dereference the sequence in place +and create the cursor in one step, call like this; +pygame.mouse.set_cursor(*pygame.cursors.arrow). + +Here is a list of available cursors; arrow, diamond, ball, + broken_x, tri_left, tri_right + +There is also a sample string cursor named 'thickarrow_strings'. +The compile() function can convert these string cursors into cursor byte data. +""" + +#default pygame black arrow +arrow = ((16, 16), (0, 0), + (0x00,0x00,0x40,0x00,0x60,0x00,0x70,0x00,0x78,0x00,0x7C,0x00,0x7E,0x00,0x7F,0x00, + 0x7F,0x80,0x7C,0x00,0x6C,0x00,0x46,0x00,0x06,0x00,0x03,0x00,0x03,0x00,0x00,0x00), + (0x40,0x00,0xE0,0x00,0xF0,0x00,0xF8,0x00,0xFC,0x00,0xFE,0x00,0xFF,0x00,0xFF,0x80, + 0xFF,0xC0,0xFF,0x80,0xFE,0x00,0xEF,0x00,0x4F,0x00,0x07,0x80,0x07,0x80,0x03,0x00)) + +diamond = ((16, 16), (7, 7), + (0, 0, 1, 0, 3, 128, 7, 192, 14, 224, 28, 112, 56, 56, 112, 28, 56, + 56, 28, 112, 14, 224, 7, 192, 3, 128, 1, 0, 0, 0, 0, 0), + (1, 0, 3, 128, 7, 192, 15, 224, 31, 240, 62, 248, 124, 124, 248, 62, + 124, 124, 62, 248, 31, 240, 15, 224, 7, 192, 3, 128, 1, 0, 0, 0)) + +ball = ((16, 16), (7, 7), + (0, 0, 3, 192, 15, 240, 24, 248, 51, 252, 55, 252, 127, 254, 127, 254, + 127, 254, 127, 254, 63, 252, 63, 252, 31, 248, 15, 240, 3, 192, 0, 0), + (3, 192, 15, 240, 31, 248, 63, 252, 127, 254, 127, 254, 255, 255, 255, + 255, 255, 255, 255, 255, 127, 254, 127, 254, 63, 252, 31, 248, 15, 240, + 3, 192)) + +broken_x = ((16, 16), (7, 7), + (0, 0, 96, 6, 112, 14, 56, 28, 28, 56, 12, 48, 0, 0, 0, 0, 0, 0, 0, 0, + 12, 48, 28, 56, 56, 28, 112, 14, 96, 6, 0, 0), + (224, 7, 240, 15, 248, 31, 124, 62, 62, 124, 30, 120, 14, 112, 0, 0, 0, + 0, 14, 112, 30, 120, 62, 124, 124, 62, 248, 31, 240, 15, 224, 7)) + + +tri_left = ((16, 16), (1, 1), + (0, 0, 96, 0, 120, 0, 62, 0, 63, 128, 31, 224, 31, 248, 15, 254, 15, 254, + 7, 128, 7, 128, 3, 128, 3, 128, 1, 128, 1, 128, 0, 0), + (224, 0, 248, 0, 254, 0, 127, 128, 127, 224, 63, 248, 63, 254, 31, 255, + 31, 255, 15, 254, 15, 192, 7, 192, 7, 192, 3, 192, 3, 192, 1, 128)) + +tri_right = ((16, 16), (14, 1), + (0, 0, 0, 6, 0, 30, 0, 124, 1, 252, 7, 248, 31, 248, 127, 240, 127, 240, + 1, 224, 1, 224, 1, 192, 1, 192, 1, 128, 1, 128, 0, 0), + (0, 7, 0, 31, 0, 127, 1, 254, 7, 254, 31, 252, 127, 252, 255, 248, 255, + 248, 127, 240, 3, 240, 3, 224, 3, 224, 3, 192, 3, 192, 1, 128)) + + + +#here is an example string resource cursor. to use this; +# curs, mask = pygame.cursors.compile_cursor(pygame.cursors.thickarrow_strings, 'X', '.') +# pygame.mouse.set_cursor((24, 24), (0, 0), curs, mask) + +thickarrow_strings = ( #sized 24x24 + "XX ", + "XXX ", + "XXXX ", + "XX.XX ", + "XX..XX ", + "XX...XX ", + "XX....XX ", + "XX.....XX ", + "XX......XX ", + "XX.......XX ", + "XX........XX ", + "XX........XXX ", + "XX......XXXXX ", + "XX.XXX..XX ", + "XXXX XX..XX ", + "XX XX..XX ", + " XX..XX ", + " XX..XX ", + " XX..XX ", + " XXXX ", + " XX ", + " ", + " ", + " ", +) + +sizer_x_strings = ( #sized 24x16 + " X X ", + " XX XX ", + " X.X X.X ", + " X..X X..X ", + " X...XXXXXXXX...X ", + "X................X ", + " X...XXXXXXXX...X ", + " X..X X..X ", + " X.X X.X ", + " XX XX ", + " X X ", + " ", + " ", + " ", + " ", + " ", +) +sizer_y_strings = ( #sized 16x24 + " X ", + " X.X ", + " X...X ", + " X.....X ", + " X.......X ", + "XXXXX.XXXXX ", + " X.X ", + " X.X ", + " X.X ", + " X.X ", + " X.X ", + " X.X ", + " X.X ", + "XXXXX.XXXXX ", + " X.......X ", + " X.....X ", + " X...X ", + " X.X ", + " X ", + " ", + " ", + " ", + " ", + " ", +) +sizer_xy_strings = ( #sized 24x16 + "XXXXXXXX ", + "X.....X ", + "X....X ", + "X...X ", + "X..X.X ", + "X.X X.X ", + "XX X.X X ", + "X X.X XX ", + " X.XX.X ", + " X...X ", + " X...X ", + " X....X ", + " X.....X ", + " XXXXXXXX ", + " ", + " ", +) +textmarker_strings = ( #sized 8x16 + "ooo ooo ", + " o ", + " o ", + " o ", + " o ", + " o ", + " o ", + " o ", + " o ", + " o ", + " o ", + "ooo ooo ", + " ", + " ", + " ", + " ", +) + + + +def compile(strings, black='X', white='.',xor='o'): + """pygame.cursors.compile(strings, black, white,xor) -> data, mask +compile cursor strings into cursor data + +This takes a set of strings with equal length and computes +the binary data for that cursor. The string widths must be +divisible by 8. + +The black and white arguments are single letter strings that +tells which characters will represent black pixels, and which +characters represent white pixels. All other characters are +considered clear. + +This returns a tuple containing the cursor data and cursor mask +data. Both these arguments are used when setting a cursor with +pygame.mouse.set_cursor(). +""" + + #first check for consistent lengths + size = len(strings[0]), len(strings) + if size[0] % 8 or size[1] % 8: + raise ValueError("cursor string sizes must be divisible by 8 %s" % + (size,)) + + for s in strings[1:]: + if len(s) != size[0]: + raise ValueError("Cursor strings are inconsistent lengths") + + #create the data arrays. + #this could stand a little optimizing + maskdata = [] + filldata = [] + maskitem = fillitem = 0 + step = 8 + for s in strings: + for c in s: + maskitem = maskitem << 1 + fillitem = fillitem << 1 + step = step - 1 + if c == black: + maskitem = maskitem | 1 + fillitem = fillitem | 1 + elif c == white: + maskitem = maskitem | 1 + elif c == xor: + fillitem = fillitem | 1 + if not step: + maskdata.append(maskitem) + filldata.append(fillitem) + maskitem = fillitem = 0 + step = 8 + return tuple(filldata), tuple(maskdata) + + + + +def load_xbm(curs, mask): + """pygame.cursors.load_xbm(cursorfile, maskfile) -> cursor_args +reads a pair of XBM files into set_cursor arguments + +Arguments can either be filenames or filelike objects +with the readlines method. Not largely tested, but +should work with typical XBM files. +""" + def bitswap(num): + val = 0 + for x in range(8): + b = num&(1<<x) != 0 + val = val<<1 | b + return val + + if type(curs) is type(''): + with open(curs) as cursor_f: + curs = cursor_f.readlines() + else: + curs = curs.readlines() + + if type(mask) is type(''): + with open(mask) as mask_f: + mask = mask_f.readlines() + else: + mask = mask.readlines() + + #avoid comments + for line in range(len(curs)): + if curs[line].startswith("#define"): + curs = curs[line:] + break + for line in range(len(mask)): + if mask[line].startswith("#define"): + mask = mask[line:] + break + #load width,height + width = int(curs[0].split()[-1]) + height = int(curs[1].split()[-1]) + #load hotspot position + if curs[2].startswith('#define'): + hotx = int(curs[2].split()[-1]) + hoty = int(curs[3].split()[-1]) + else: + hotx = hoty = 0 + + info = width, height, hotx, hoty + + for line in range(len(curs)): + if curs[line].startswith('static char') or curs[line].startswith('static unsigned char'): + break + data = ' '.join(curs[line+1:]).replace('};', '').replace(',', ' ') + cursdata = [] + for x in data.split(): + cursdata.append(bitswap(int(x, 16))) + cursdata = tuple(cursdata) + + for line in range(len(mask)): + if mask[line].startswith('static char') or mask[line].startswith('static unsigned char'): + break + data = ' '.join(mask[line+1:]).replace('};', '').replace(',', ' ') + maskdata = [] + for x in data.split(): + maskdata.append(bitswap(int(x, 16))) + maskdata = tuple(maskdata) + return info[:2], info[2:], cursdata, maskdata diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/cursors.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/cursors.pyi new file mode 100644 index 0000000..3d79635 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/cursors.pyi @@ -0,0 +1,121 @@ +from typing import Tuple, Sequence, Optional + +_Bitmap = Tuple[ + Tuple[int, int], + Tuple[int, int], + Tuple[ + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + ], + Tuple[ + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + ], +] +_Small_string = Tuple[ + str, str, str, str, str, str, str, str, str, str, str, str, str, str, str, str +] +_Big_string = Tuple[ + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, +] + +arrow: _Bitmap +diamond: _Bitmap +broken_x: _Bitmap +tri_left: _Bitmap +tri_right: _Bitmap +thickarrow_strings: _Big_string +sizer_x_strings: _Small_string +sizer_y_strings: _Big_string +sizer_xy_strings: _Small_string + +def compile( + strings: Sequence[str], + black: Optional[str] = "X", + white: Optional[str] = ".", + xor="o", +) -> Tuple[Sequence[int], Sequence[int]]: ... +def load_xbm(cursorfile: str, maskfile: str): ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/display.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/display.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..9b4317b Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/display.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/display.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/display.pyi new file mode 100644 index 0000000..0ac7df8 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/display.pyi @@ -0,0 +1,79 @@ +from typing import Union, Tuple, List, Optional, Dict, Sequence + +from pygame.color import Color +from pygame.surface import Surface +from pygame.rect import Rect +from pygame.math import Vector2 +from pygame.constants import FULLSCREEN + +_Coordinate = Union[Tuple[float, float], List[float], Vector2] +_RectValue = Union[ + Rect, + Union[Tuple[int, int, int, int], List[int]], + Union[Tuple[_Coordinate, _Coordinate], List[_Coordinate]], +] +_ColorValue = Union[ + Color, int, Tuple[int, int, int], Tuple[int, int, int, int], List[int] +] + +class _VidInfo: + hw: int + wm: int + video_mem: int + bitsize: int + bytesize: int + masks: Tuple[int, int, int, int] + shifts: Tuple[int, int, int, int] + losses: Tuple[int, int, int, int] + blit_hw: int + blit_hw_CC: int + blit_hw_A: int + blit_sw: int + blit_sw_CC: int + blit_sw_A: int + current_h: int + current_w: int + +def init() -> None: ... +def quit() -> None: ... +def get_init() -> bool: ... +def set_mode( + size: Optional[Union[Tuple[int, int], Sequence[int]]], + flags: Optional[int] = 0, + depth: Optional[int] = 0, + display: Optional[int] = 0, +) -> Surface: ... +def get_surface() -> Surface: ... +def flip() -> None: ... +def update(rectangle: Optional[Union[_RectValue, List[_RectValue]]] = None) -> None: ... +def get_driver() -> str: ... +def Info() -> _VidInfo: ... +def get_wm_info() -> Dict[str, int]: ... +def list_modes( + depth: Optional[int] = 0, + flags: Optional[int] = FULLSCREEN, + display: Optional[int] = 0, +) -> List[Tuple[int, int]]: ... +def mode_ok( + size: Union[Sequence[int], Tuple[int, int]], + flags: Optional[int] = 0, + depth: Optional[int] = 0, + display: Optional[int] = 0, +) -> int: ... +def gl_get_attribute(flag: int) -> int: ... +def gl_set_attribute(flag: int, value: int) -> None: ... +def get_active() -> int: ... +def iconify() -> int: ... +def toggle_fullscreen() -> int: ... +def set_gamma( + red: float, green: Optional[float] = None, blue: Optional[float] = None +) -> int: ... +def set_gamma_ramp( + red: Sequence[int], green: Sequence[int], blue: Sequence[int] +) -> int: ... +def set_icon(surface: Surface) -> None: ... +def set_caption(title: str, icontitle: Optional[str] = None) -> None: ... +def get_caption() -> Tuple[str, str]: ... +def set_palette(palette: Sequence[_ColorValue]) -> None: ... +def get_num_displays() -> int: ... +def get_window_size() -> Tuple[int, int]: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/docs/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/docs/__init__.py new file mode 100644 index 0000000..0d02f17 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/docs/__init__.py @@ -0,0 +1,12 @@ +# Make docs a package that brings up the main page in a web brower when +# executed. +# +# python -m pygame.docs + +if __name__ == '__main__': + import os + pkg_dir = os.path.dirname(os.path.abspath(__file__)) + main = os.path.join(pkg_dir, '__main__.py') + exec(open(main).read()) + + diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/docs/__main__.py b/Display/.venv/lib/python3.7/site-packages/pygame/docs/__main__.py new file mode 100644 index 0000000..5fa3d2d --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/docs/__main__.py @@ -0,0 +1,28 @@ +# python -m pygame.docs + +import os +import webbrowser +try: + from urllib.parse import urlunparse, quote +except ImportError: + from urlparse import urlunparse + from urllib import quote + +def iterpath(path): + path, last = os.path.split(path) + if last: + for p in iterpath(path): + yield p + yield last + +pkg_dir = os.path.dirname(os.path.abspath(__file__)) +main_page = os.path.join(pkg_dir, 'index.html') +if os.path.exists(main_page): + url_path = quote('/'.join(iterpath(main_page))) + drive, rest = os.path.splitdrive(__file__) + if drive: + url_path = "%s/%s" % (drive, url_path) + url = urlunparse(('file', '', url_path, '', '', '')) +else: + url = "https://www.pygame.org/docs/" +webbrowser.open(url) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/docs/logos.html b/Display/.venv/lib/python3.7/site-packages/pygame/docs/logos.html new file mode 100644 index 0000000..0c08249 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/docs/logos.html @@ -0,0 +1,44 @@ +<html> <title>Pygame Logos + + + + + +

pygame logos
+ +These logos are available for use in your own game projects. +Please put them up wherever you see fit. The logo was created +by TheCorruptor on July 29, 2001. + + +
+ +

+There is a higher resolution layered photoshop image +available here. +(1.3 MB)

+ +
+
+ pygame_logo.gif - 676 x 200
+

+
+ pygame_small.gif - 338 x 100
+

+
+ pygame_tiny.gif - 200 x 60
+
+ +

+
+pygame_powered.gif - 250 x 100
+


 
+ + + + + + + diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/docs/pygame_logo.gif b/Display/.venv/lib/python3.7/site-packages/pygame/docs/pygame_logo.gif new file mode 100644 index 0000000..63d2e77 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/docs/pygame_logo.gif differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/docs/pygame_powered.gif b/Display/.venv/lib/python3.7/site-packages/pygame/docs/pygame_powered.gif new file mode 100644 index 0000000..5a2bb5f Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/docs/pygame_powered.gif differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/docs/pygame_small.gif b/Display/.venv/lib/python3.7/site-packages/pygame/docs/pygame_small.gif new file mode 100644 index 0000000..4916dbf Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/docs/pygame_small.gif differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/docs/pygame_tiny.gif b/Display/.venv/lib/python3.7/site-packages/pygame/docs/pygame_tiny.gif new file mode 100644 index 0000000..f9aa517 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/docs/pygame_tiny.gif differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/docs/ref/docscomments.json b/Display/.venv/lib/python3.7/site-packages/pygame/docs/ref/docscomments.json new file mode 100644 index 0000000..e171489 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/docs/ref/docscomments.json @@ -0,0 +1,5273 @@ +[ + { + "datetimeon" : "2005-11-11T14:05:52", + "id" : 3, + "content" : "How To Get ALL The Mouse Clicks.\n\nTook me hours to figure this out. Also note that button 1 and 3 pressed at the same time shows up as button 2, at least on my ubuntu computer.\n\ne=pygame.event.wait()\nif e.type == MOUSEBUTTONDOWN and e.button == 4 : do something mousey", + "user_title" : "Douglas Smith", + "link" : "pygame.mouse.get_pressed" + }, + { + "link" : "pygame.Surface", + "user_title" : "Marius Gedminas", + "content" : "If you're trying to create a surface with per-pixel alphas, and\n\n my_surface = pygame.Surface((w, h), SRCALPHA)\n\ncreates a regular surface instead, try\n\n my_surface = pygame.Surface((w, h)).convert_alpha()", + "id" : 39, + "datetimeon" : "2006-01-05T16:07:06" + }, + { + "link" : "pygame.font.get_fonts", + "user_title" : "Dave Burton", + "content" : "Interestingly, pygame.font.get_default_font() returns a font name ('freesansbold.ttf') which is not among the 189 listed by pygame.font.get_fonts().", + "id" : 3698, + "datetimeon" : "2011-01-03T08:47:41" + }, + { + "id" : 3699, + "datetimeon" : "2011-01-03T09:13:14", + "link" : "pygame.font.SysFont", + "user_title" : "Dave Burton", + "content" : "The font name is not a list! It is a single string.\n\nThe string can contain multiple font names with commas between them,\nbut if you pass a Python list (or tuple) you'll get an error." + }, + { + "link" : "pygame.PixelArray", + "user_title" : "Dave Burton", + "content" : "Re: \"During its lifetime, the PixelArray locks the surface, thus you explicitly have to delete it once its not used anymore and the surface should perform operations in the same scope.\"\n\n1. Grammer: s/its/it's/\n\n2. s/you explicitly have to delete/you have to explicitly delete/\n\n3. I assume that to explicitly delete it you can either use \"del pxarray\"\nor else simply exit the function to which pxarray is local. Is that correct?\n\n4. What does \"and the surface should perform operations in the same scope\" mean?\nIs it saying something about the surface returned by px.make_surface(), i.e.,\nthat it should be a local variable in the same function to which pxarray is local?\nOr is it saying something about the surface that is passed to pygame.PixelArray()\nto create the pxarray object, and if so WHAT is it saying?", + "id" : 3703, + "datetimeon" : "2011-01-07T03:08:20" + }, + { + "user_title" : "Dave Burton", + "content" : "On my Windows Vista machine running Python 3.1.2 and pygame 1.9.1, pgame.font.get_fonts() returns a list of 189 fonts. All the font names are lower case, and there are no special characters (like hyphens) in the names. The expected 'timesnewroman', 'arial', 'arialblack', 'couriernew', 'veranda', 'microsoftsansserif', 'symbol' and 'wingdings' are there (but not 'times' or 'roman' or 'helvetica'), but also many obscure fonts that I've never heard of.", + "link" : "pygame.font.get_fonts", + "datetimeon" : "2011-01-03T08:43:54", + "id" : 3697 + }, + { + "link" : "pygame.key.get_pressed", + "content" : "Pretty cool demo Mr. Anony", + "user_title" : "Robert Leachman", + "id" : 3683, + "datetimeon" : "2010-12-10T22:09:50" + }, + { + "link" : "pygame.locals", + "content" : "If you want to see a list of attributes, do a help(pygame) and it'll show you", + "user_title" : "Alex Polosky", + "id" : 3686, + "datetimeon" : "2010-12-15T23:46:38" + }, + { + "link" : "pygame.display.init", + "user_title" : "Robert Leachman", + "content" : "Works fine for me on OS X 10.6.5, though yes it does need to brought up to Quartz", + "id" : 3675, + "datetimeon" : "2010-12-04T21:54:48" + }, + { + "datetimeon" : "2007-03-05T00:13:41", + "id" : 403, + "user_title" : "Ian Mallett", + "content" : "See tutorials. \nAfter each line \n pygame.image.load(\"<>\")\nMake it\n pygame.image.load(\"<>\").convert()\nNo matter what, this will increase your speed by 600%!\nThanks to whoever put in that tutorial!\n -P.Z.", + "link" : "pygame.draw" + }, + { + "id" : 439, + "datetimeon" : "2007-03-17T13:13:59", + "link" : "pygame.draw", + "content" : "see:\nhttp://www.pygame.org/docs/tut/newbieguide.html\n#4", + "user_title" : "Ian Mallett" + }, + { + "link" : "pygame.movie", + "user_title" : "Jordan Trudgett", + "content" : "Dear readers, here is a working example of MPEG playing.\n-tgfcoder\n\n\nimport pygame, time\n\npygame.init()\n\ncine = pygame.movie.Movie('a-movie.mpg')\nsz=cine.get_size()\npygame.display.set_mode(sz)\nscreen = pygame.display.get_surface()\ncine.set_display(screen)\ncine.play()\nwhile True:\n time.sleep(1)", + "id" : 1349, + "datetimeon" : "2008-01-01T09:40:25" + }, + { + "link" : "pygame.movie", + "content" : "Oh, please replace pygame.init() with pygame.display.init()\nBecause we don't want the mixer to be initialised.", + "user_title" : "Jordan Trudgett", + "id" : 1350, + "datetimeon" : "2008-01-01T09:46:24" + }, + { + "datetimeon" : "2007-04-20T18:04:08", + "id" : 503, + "user_title" : "Guillaume Rava", + "content" : "Well, actually it's not even that (x,y) needs to be in the referential of the Rect, because if it was true, then (0,0) would return 1, and it doesn't. It is really a bug.", + "link" : "Rect.collidepoint" + }, + { + "datetimeon" : "2008-01-10T10:08:04", + "id" : 1392, + "content" : "# Ellipse example:\n# When border=0 ellipse is filled\n# (screen, (rgb colour) (Xpos,Ypos,width,height),border width)\npygame.draw.ellipse(screen, (0, 127, 0), (300, 150, 80, 40), 0)", + "user_title" : "Miroslav Cika", + "link" : "pygame.draw.ellipse" + }, + { + "link" : "pygame.draw.circle", + "user_title" : "Miroslav Cika", + "content" : "# Circle example:\n# When border=0 circle is filled\n# (screen, (rgb colour), (Xpos,Ypos),Diameter,border width)\npygame.draw.circle(screen, (0, 127, 255), (300, 140), 50, 4)", + "id" : 1393, + "datetimeon" : "2008-01-10T10:20:38" + }, + { + "id" : 1407, + "datetimeon" : "2008-01-13T13:38:10", + "link" : "pygame.movie", + "content" : "thank you Trudget for the working code", + "user_title" : "vishwanath" + }, + { + "id" : 1420, + "datetimeon" : "2008-01-16T15:33:48", + "link" : "Rect.collidepoint", + "user_title" : "Tim Winter", + "content" : "\"\"\"It seems that this method does not detect point collisions that fall anywhere \nalong the right wall or bottom wall of the rect used. The following program\ncreates a rect with a width and height of 4, and a topleft corner at [0,0]. \nThe program then moves along each row of the rect area from left to right and\ntop to bottom by 1 unit, creating a new point and checking to see if the point\ncollides with the rect. If the point collides, a 1 is printed, and if the\npoint doesn't collide, a 0 is printed.\"\"\"\n\n# import\nimport pygame\n\n# main\ny = 4\nr = pygame.Rect(0,0,y,y)\np = [0,0]\npList = []\nwhile p != [0,y+1]:\n\tfor n in range(0,y+1):\n\t\tp[0] = n\n\t\tif r.collidepoint(p):\n\t\t\tpList.append(1)\n\t\telse:\n\t\t\tpList.append(0)\n\n\tprint '%d %d %d %d %d' % (pList[0],pList[1],pList[2],pList[3],pList[4])\n\t\n\tpList = []\n\tp[0] = 0\n\tp[1] += 1\n\n# wait for user to manually exit program\ninput('press enter to exit')\n\n\"\"\"Here is the output:\"\"\"\n1 1 1 1 0\n1 1 1 1 0\n1 1 1 1 0\n1 1 1 1 0\n0 0 0 0 0\npress enter to exit\n\n\"\"\"Even if you were to directly reference the topright, bottomleft, or bottomright\npoint of the rect as the argument to the collidepoint function, the rect still \nwould not detect a collision. The rect does, however, detect collision with its\ntopleft point:\"\"\"\n\n>>>r.collidepoint(r.bottomleft)\n0\n>>>r.collidepoint(r.topright)\n0\n>>>r.collidepoint(r.bottomright)\n0\n>>>r.collidepoint(r.topleft)\n1" + }, + { + "datetimeon" : "2008-01-25T15:59:11", + "id" : 1442, + "user_title" : "Ian Mallett", + "content" : "To use the scrollwheel:\nfor event in pygame.event.get():\n if event.type == MOUSEBUTTONDOWN:\n if event.button == 4:\n #Zoom Out\n elif event.button == 5:\n #Zoom In", + "link" : "pygame.mouse" + }, + { + "user_title" : "Jeiel Aranal", + "content" : "Use the following class to generate a bezier curve that can be drawn with aalines:\n\n## Class begins here\nclass Bezier:\n\n\tclass SmoothnessError(Exception): pass\n\tclass CurveError(Exception): pass\n\n\tdef __init__(self):\n\t\t\"\"\"\n\t\tA Python class for generating bezier curves\n\t\t\n\t\tAn implementation of an algorithm presented by Nils Pipenbrinck\n\t\thttp://www.cubic.org/docs/bezier.htm\n\t\t\"\"\"\n\t\n\tdef __lerp(self, ptA, ptB, t):\n\t\t\"\"\"\n\t\tReturns the linear interp between two points as a list\n\t\tptA and ptB are a list of xy coords, t is the point on the curve\n\t\t\"\"\"\n\t\tdest = []\n\t\tdest.append(ptA[0]+float(ptB[0]-ptA[0])*t)\n\t\tdest.append(ptA[1]+float(ptB[1]-ptA[1])*t)\n\t\treturn dest\n\t\n\tdef bezierPt(self, ctrlPts, t):\n\t\t\"\"\"A recursive function for finding point t along a bezier curve\"\"\"\n\t\tif len(ctrlPts) == 1:\n\t\t\t#print \"Len is 1\", ctrlPts\n\t\t\treturn ctrlPts[0]\n\t\tlerpList = []\n\t\tfor i in xrange(len(ctrlPts)-1):\n\t\t\tptA = [ctrlPts[i][0],ctrlPts[i][1]]\n\t\t\tptB = [ctrlPts[i+1][0],ctrlPts[i+1][1]]\n\t\t\tlerpList.append(self.__lerp(ptA,ptB,t))\n\t\t#print len(lerpList)\n\t\treturn self.bezierPt(lerpList, t)\n\t\n\tdef makeBezier(self, ctrlPts, smoothness):\n\t\t\"\"\"\n\t\tReturns a list of points on a bezier curve\n\t\t\n\t\tctrlPts is a list of 2d Points that define the curve, in most cases these\n\t\tconsist of control point locations and their handles, except in a 3 point\n\t\tcurve where it's just defined by the three control points.\n\t\t\n\t\tsmoothness is the number of points on the curve that should be generated.\n\t\tThis should always be more than two points or generating the bezier curve is\n\t\tpointless and the script dies in a fire (or throws an exception)\n\t\t\"\"\"\n\t\t\n\t\tif len(ctrlPts) < 2:\n\t\t\traise self.CurverError(\"Curve list must contain more than one point\")\n\t\tif smoothness < 3:\n\t\t\traise self.SmoothnessError(\"Smoothness must be more than two\")\n\t\titeration = smoothness\n\t\tbezierList = []\n\t\tsubtract=1.0/smoothness\n\t\tfor i in xrange(0,iteration):\n\t\t\tt = 1.0-(subtract*i)\n\t\t\tif t < subtract:\n\t\t\t\tt = 0\n\t\t\tbPt = self.bezierPt(ctrlPts,t)\n\t\t\t#print bPt\n\t\t\tbezierList.append(bPt)\n\t\treturn bezierLis\n## Class ends\n\n###################\n# An example of how to use the class with pygame\n\n\n## Pygame Example\nimport math, pygame\nfrom pygame.locals import *\nimport bezier\n\ndef main():\n pygame.init()\n screen = pygame.display.set_mode((640,480))\n clock = pygame.time.Clock()\n \n b = bezier.Bezier()\n \"\"\"\n\tA bezier curve definition, a list of 2d poins, simple innit\n\tIt's basically control points with control handle locations before or\n\tafter the control point.\n\t\n Read http://www.cubic.org/docs/bezier.htm for more info\n \"\"\"\n bezierPts = [[40,100],[80,20],[150,180],[260,100]]\n bLine = b.makeBezier(bezierPts, 10)\n screen.fill((255,255,255))\n pygame.draw.aalines(screen, (1,1,1), False, bLine, 1)\n pygame.display.flip()\n bounce = False\n \n while True:\n clock.tick(60)\n pygame.event.pump()\n event = pygame.event.poll()\n if event.type == QUIT:\n return\n if event.type == KEYDOWN:\n if event.key == K_ESCAPE:\n return\n setTo = pygame.time.get_ticks()/20\n bezierPts[1][1] = setTo\n bLine = b.makeBezier(bezierPts,20)\n screen.fill((255,255,255))\n pygame.draw.aalines(screen, (1,1,1), False, bLine, 1)\n pygame.display.flip()\n\nif __name__ == \"__main__\":\n m = main()\n## End example", + "link" : "pygame.draw.aalines", + "datetimeon" : "2008-02-06T10:50:10", + "id" : 1502 + }, + { + "id" : 635, + "datetimeon" : "2007-06-14T12:13:28", + "link" : "Sound.play", + "content" : "Play can return None. So be sure to check the channel before using it. Something like this...\n\n channel = self.bounce_sound.play()\n if channel is not None:\n channel.set_volume(1.0 - stereo, stereo)", + "user_title" : "Will McGugan" + }, + { + "id" : 676, + "datetimeon" : "2007-06-30T19:41:18", + "link" : "Sound.play", + "content" : "Should have an optional option \nfor sound playback speed...", + "user_title" : "Ian Mallett" + }, + { + "id" : 689, + "datetimeon" : "2007-07-03T01:18:12", + "link" : "Font.render", + "user_title" : "Ian Mallett", + "content" : "When antialias is enabled, rendering it on a black background makes it look bold." + }, + { + "datetimeon" : "2008-02-25T23:09:09", + "id" : 1646, + "user_title" : "Ian Mallett", + "content" : "Should have an Anti-alias option...", + "link" : "pygame.draw.circle" + }, + { + "content" : "mods = pygame.key.get_mods()\nif mods & KMOD_LSHIFT: #use whatever KMOD_ constant you want;)\n print \"left shift pressed\"", + "user_title" : "Isaiah Heyer", + "link" : "pygame.key.get_mods", + "datetimeon" : "2008-03-29T16:22:04", + "id" : 1716 + }, + { + "datetimeon" : "2008-03-29T23:08:21", + "id" : 1718, + "user_title" : "Ian Mallett", + "content" : "I would like to have a method of telling which side of a rect a point collides. \nIn other words, which side is the point closest to?", + "link" : "Rect.collidepoint" + }, + { + "datetimeon" : "2007-08-01T17:48:16", + "id" : 785, + "content" : "Right. Unfortunately, That's the way it is. A width or height of 0 should also\nbe allowed, for rectangles of changing sizes (think progressbar at 0%)", + "user_title" : "Ian Mallett", + "link" : "pygame.draw.rect" + }, + { + "datetimeon" : "2007-09-23T08:07:45", + "id" : 873, + "content" : "Instead of drawing a circle with zero radius, you can use the method set_at on the surface to set the color of a single pixel: http://www.pygame.org/docs/ref/surface.html#Surface.set_at", + "user_title" : "Victor Blomqvist", + "link" : "pygame.draw.circle" + }, + { + "link" : "pygame.display.init", + "content" : "'dummy' driver is missing ;-)", + "user_title" : "DR0ID", + "id" : 875, + "datetimeon" : "2007-09-23T12:08:05" + }, + { + "id" : 882, + "datetimeon" : "2007-09-27T02:05:16", + "link" : "pygame.display.init", + "user_title" : "aaron pedralvez", + "content" : "please able my display mode with opengl acceleration" + }, + { + "link" : "pygame.event", + "user_title" : "Andy Hanson", + "content" : "In the event MOUSEBUTTONDOWN, if you're using a mouse with a rotating wheel,\nevent.button returns 4 when it is rotated forward (counterclockwise) and 5 when\nit is rotating backward (clockwise). I used a print statement to discover this.", + "id" : 900, + "datetimeon" : "2007-10-02T20:43:15" + }, + { + "user_title" : "Andy Hanson", + "content" : "In the event MOUSEBUTTONDOWN, if you're using a mouse with a rotating wheel,\nevent.button returns 4 when it is rotated forward (counterclockwise) and 5 when\nit is rotating backward (clockwise). I used a print statement to discover this.", + "link" : "pygame.event", + "datetimeon" : "2007-10-02T20:43:21", + "id" : 901 + }, + { + "datetimeon" : "2007-12-04T14:43:28", + "id" : 1206, + "user_title" : "Andy Sommerville", + "content" : "You can request fullscreen, but there doesn't seem to be a way to\ndetermine whether it's on. Meaning, there ought to be a 'get_mode()'.", + "link" : "pygame.display.set_mode" + }, + { + "link" : "pygame.mixer.Sound", + "user_title" : "Ian Mallett", + "content" : "http://www.pygame.org/docs/ref/sndarray.html#pygame.sndarray.make_sound\ncan be used to synthesize a sound object from sound samples.", + "id" : 1953, + "datetimeon" : "2008-05-26T20:16:58" + }, + { + "id" : 1917, + "datetimeon" : "2008-05-21T14:15:44", + "link" : "pygame.movie", + "content" : "The movie module in Pygame 1.8 works on Windows.\nThe statement that it doesn't work is out-of-date.", + "user_title" : "Jason M. Marshall" + }, + { + "content" : "return bezierLis -> return bezierList (line 65)", + "user_title" : "Jordan Trudgett", + "link" : "pygame.draw.aalines", + "datetimeon" : "2008-06-18T02:46:43", + "id" : 2060 + }, + { + "id" : 2150, + "datetimeon" : "2008-07-10T13:15:18", + "link" : "pygame.mixer.get_num_channels", + "content" : "pygame.mixer.get_num_channels(): return count", + "user_title" : "Jordan Trudgett" + }, + { + "id" : 2156, + "datetimeon" : "2008-07-11T23:25:11", + "link" : "Surface.set_at", + "user_title" : "Ian Mallett", + "content" : "Calling Surface.lock() before many calls to Surface.set_at() and Surface.unlock() after is a great and easy optimization." + }, + { + "link" : "pygame.key.set_repeat", + "user_title" : "Ian Mallett", + "content" : "Just set the delay to something really big.", + "id" : 2265, + "datetimeon" : "2008-08-19T06:01:04" + }, + { + "id" : 2339, + "datetimeon" : "2008-11-28T19:09:39", + "link" : "pygame.event.post", + "user_title" : "Weeble", + "content" : "Is this thread-safe? Can I safely post messages from a different thread\nfrom the one that's processing events and rendering?" + }, + { + "link" : "Surface.set_alpha", + "content" : "''' Change alpha for surfaces with per-pixel alpha; only for small surfaces '''\ndef change_alpha(surface,alpha=0.5):\n\tsize = surface.get_size()\n\ttry:\n\t\tfor y in xrange(size[1]):\n\t\t\tfor x in xrange(size[0]):\n\t\t\t\tr,g,b,a = surface.get_at((x,y))\n\t\t\t\tsurface.set_at((x,y),(r,g,b,int(a*alpha)))\n\texcept:\n\t\treturn surface\n\treturn surface", + "user_title" : "Josef Vanzura", + "id" : 3245, + "datetimeon" : "2010-11-19T09:47:18" + }, + { + "link" : "Surface.set_alpha", + "content" : "You can also do it with surfarray (faster).", + "user_title" : "Josef Vanzura", + "id" : 3246, + "datetimeon" : "2010-11-19T09:49:02" + }, + { + "datetimeon" : "2010-11-19T09:56:18", + "id" : 3247, + "content" : "Sorry. I didn't read the previous comment, which is a better way.", + "user_title" : "Josef Vanzura", + "link" : "Surface.set_alpha" + }, + { + "user_title" : "Shanti Pothapragada", + "content" : "present in pygame 1.9.1 but not in pygame 1.8.1, which is currently the last binary release on Linux.", + "link" : "Rect.copy", + "datetimeon" : "2010-11-22T17:04:34", + "id" : 3249 + }, + { + "link" : "pygame.Rect", + "user_title" : "Sam Bull", + "content" : "Also includes the attributes: x, y.", + "id" : 3225, + "datetimeon" : "2010-10-26T07:40:18" + }, + { + "datetimeon" : "2010-09-29T19:26:57", + "id" : 3211, + "content" : "Works like a charm. Thanks whoever you are.", + "user_title" : "Bartosz Debski", + "link" : "Surface.fill" + }, + { + "link" : "pygame.movie", + "content" : "The code snippet works perfectly; thanks!\nI think the documentation is sorely in need of an update.\nWishlist: other video formats, like .avi?", + "user_title" : "Ian Mallett", + "id" : 2360, + "datetimeon" : "2009-01-01T16:05:17" + }, + { + "id" : 2366, + "datetimeon" : "2009-01-07T12:38:26", + "link" : "pygame.sprite.RenderUpdates", + "content" : "An example to use this:\nscreen = pygame.display.set_mode(SCREENRECT.size) # SCREENRECT is a rect variable...\n # ...with screen dimension\n\ndoggie = pygame.sprite.RenderUpdates() #We create the group\nDog.containers = doggie \n# class Dog: Needs 'pygame.sprite.Sprite.__init__(self,self.containers)'\n# inside def __init__(self, ...):\n\ndog1 = Dog(...) #Class Dog\ndog2 = Dog(...)\n...\ndogN = Dog(...)\n\n... #Some move actions and things\n\n#Now, time to re-paint them all\ndoggie.clear(screen, Background)\nchanges = doggie.draw(screen)\npygame.display.update(changes)\n#Now we have all dogs updated in screen\n\n#---------\nEasy, quick and effortless", + "user_title" : "Patata" + }, + { + "user_title" : "Nekody Lenkner", + "content" : "# A better loading script:\n\nimport os, pygame\n\ndef load_image(file_name, colorkey=False, image_directory='images'):\n 'Loads an image, file_name, from image_directory, for use in pygame'\n file = os.path.join(image_directory, file_name)\n _image = pygame.image.load(file)\n if colorkey:\n if colorkey == -1: \n # If the color key is -1, set it to color of upper left corner\n colorkey = _image.get_at((0, 0))\n _image.set_colorkey(colorkey)\n _image = _image.convert()\n else: # If there is no colorkey, preserve the image's alpha per pixel.\n _image = _image.convert_alpha()\n return _image", + "link" : "pygame.image.load", + "datetimeon" : "2009-03-20T21:58:08", + "id" : 2399 + }, + { + "id" : 3151, + "datetimeon" : "2010-07-02T01:21:30", + "link" : "pygame.font.SysFont", + "user_title" : "Mad Cloud Games", + "content" : "what does it mean by font name? can it be a path to a font?" + }, + { + "datetimeon" : "2009-08-01T23:26:07", + "id" : 2900, + "content" : "You can use multiple screens, but you'll need to make a separate process for each.", + "user_title" : "Ian Mallett", + "link" : "pygame.display.set_mode" + }, + { + "link" : "pygame.event", + "user_title" : "DR0ID", + "content" : "VIDEORESIZE size, w, h\nsize == (w, h) # same data, different access", + "id" : 2411, + "datetimeon" : "2009-04-04T12:27:05" + }, + { + "id" : 2902, + "datetimeon" : "2009-08-03T01:50:34", + "link" : "Surface.copy", + "user_title" : "josmiley", + "content" : "je ne sais pas pourquoi, mais; si vous utiliser une surface pour effacer le display au lieu d'utiliser un 'fill',\nil sera beaucoup plus rapide de blitter une copie du display :\n\ndisplay = pygame.display.set_mode((500,500))\nbackground = pygame.image.load('blablabla...')\ndisplay.blit(background,(0,0))\nbackground = display.copy() ----> utiliser cette copie pour multi-blitter plus rapidement une image de fond." + }, + { + "id" : 2896, + "datetimeon" : "2009-07-29T00:20:38", + "link" : "pygame.event.Event", + "user_title" : "Daniel Westbrook", + "content" : "This doesn't say anything about the type attribute.\nYou can compare it to MOUSEBUTTONUP, KEYDOWN, etc to find out what the events\ntype is." + }, + { + "datetimeon" : "2010-05-04T17:47:38", + "id" : 3117, + "user_title" : "Alex", + "content" : "is it a Rect object???", + "link" : "pygame.display.update" + }, + { + "content" : "TIP:\nIf sound has noise/noisy is choppy or has static, the solution:\n\npygame.mixer.quit() #Make sure you all this before .init()\npygame.mixer.init()", + "user_title" : "Chris Goldie", + "link" : "Sound.play", + "datetimeon" : "2009-08-11T05:44:03", + "id" : 2911 + }, + { + "datetimeon" : "2010-04-29T02:55:18", + "id" : 3113, + "content" : "The convert_alpha function prepares a surface for usage with per-pixel alphas. That is, for example, if you have a PNG or TGA image with an alpha channel controlling opacity of individual pixels, you would want to use this function on your surface after loading the image to speed up the blitting process.", + "user_title" : "Brad Smithee", + "link" : "Surface.convert_alpha" + }, + { + "content" : "This will be extremely useful!", + "user_title" : "Ian Mallett", + "link" : "pygame.transform.average_surfaces", + "datetimeon" : "2009-08-15T19:21:45", + "id" : 2917 + }, + { + "id" : 2887, + "datetimeon" : "2009-07-20T07:29:06", + "link" : "Font.render", + "content" : "Some basic sample code for (approximately) constraining a bunch of text to a given width:\n\nwordsToWrite = toWrite.rstrip().split(\" \") #Get rid of the newline char and split on spaces\ncurrLine = \"\"\nnumLines = 0\nmaxWidthFound = 0\nfor word in wordsToWrite:\n currLine = currLine + \" \" + word #Add the next word to the line\n\n if ((textFont.size(currLine))[0] > maxAllowedWidth): #Check if the width of the line exceeds the set limit\n\n if (textFont.size(currLine))[0] > maxWidthFound: #Get the maximum line width found\n maxWidthFound = (textFont.size(currLine))[0]\n\n lines.append (textFont.render(currLine, 1, color, bgcolor)) #Add the rendered line to a list\n currLine = \"\"\n numLines = numLines + 1\n\nif currLine != \"\": #Once we exit the loop, we will probably still have a line to be rendered\n lines.append (textFont.render(currLine, 1, color, bgcolor))\n currLine = \"\"\n numLines = numLines + 1\n\nself.image = pygame.Surface((maxWidthFound + 20, numLines * textFont.get_height() + 20)) #Create a surface of the appropriate size\n\nfor lineNum in range(numLines): \n self.image.blit(lines[lineNum], (10,lineNum * textFont.get_height() + 10))", + "user_title" : "Aditya Keswani" + }, + { + "id" : 2886, + "datetimeon" : "2009-07-20T07:23:36", + "link" : "pygame.draw", + "user_title" : "Aditya Keswani", + "content" : "For all of these drawing functions, the coordinates are relative to the surface\nyou are drawing to. i.e. if you are drawing to a surface somewhere in the middle of\nthe screen, and you draw a circle at (0,0), its center will be the top-left corner\nof the surface being drawn to, not the top-left corner of the screen" + }, + { + "datetimeon" : "2010-03-25T15:42:06", + "id" : 3087, + "content" : "The messages here:\nhttp://www.mail-archive.com/pygame-users@seul.org/msg10616.html\n\nimply that GL_SWAP_CONTROL can also be passed to gl_set_attribute to control whether\ndisplay swaps honor vsync.", + "user_title" : "Jonathan Hartley", + "link" : "pygame.display.gl_set_attribute" + }, + { + "link" : "pygame.Color", + "content" : "Pygame THECOLORS as HTML\nhttps://sites.google.com/site/meticulosslacker/pygame-thecolors", + "user_title" : "Meticulos Slacker", + "id" : 3078, + "datetimeon" : "2010-03-18T03:10:26" + }, + { + "id" : 3079, + "datetimeon" : "2010-03-19T00:36:48", + "link" : "pygame.mixer.pre_init", + "content" : "Should be \"buffer\", not \"buffersize\"", + "user_title" : "Ian Mallett" + }, + { + "link" : "pygame.key.set_repeat", + "content" : "Put this first:\nfor e in pygame.event.get()", + "user_title" : "Ian Mallett", + "id" : 3080, + "datetimeon" : "2010-03-20T19:51:20" + }, + { + "link" : "pygame.event", + "content" : "mod is the bitfield of KMOD_* constants:\npygame.KMOD_NONE\t0\npygame.KMOD_LSHIFT\t1\npygame.KMOD_RSHIFT\t2\npygame.KMOD_SHIFT\t3\npygame.KMOD_LCTRL\t64\npygame.KMOD_RCTRL\t128\npygame.KMOD_CTRL\t192\npygame.KMOD_LALT\t256\npygame.KMOD_RALT\t512\npygame.KMOD_ALT\t\t768\npygame.KMOD_LMETA\t1024\npygame.KMOD_RMETA\t2048\npygame.KMOD_META\t3072\npygame.KMOD_NUM\t\t4096\npygame.KMOD_CAPS\t8192\npygame.KMOD_MODE\t16384", + "user_title" : "Vladar", + "id" : 3081, + "datetimeon" : "2010-03-23T06:16:44" + }, + { + "link" : "Channel.queue", + "user_title" : "Andy Hanson", + "content" : "If you try to use fadeout, the queued sound will begin, as opposed to stop and pause.", + "id" : 2875, + "datetimeon" : "2009-07-12T22:43:31" + }, + { + "id" : 2928, + "datetimeon" : "2009-08-29T04:11:27", + "link" : "pygame.key.name", + "user_title" : "Jared", + "content" : "I ran into that problem -- the solution is to initialize pygame first :)\n\nimport pygame\npygame.init()\nprint pygame.key.name(pygame.K_UP)" + }, + { + "user_title" : "David Khono Hackland", + "content" : "It appears that when the delay is set to zero, \nkey.set_repeat is returned to the default, disabled state.\nTo set it to a minimum, essentially no delay, just set it to 1.\n\npygame.key.set_repeat(0,50) #Doesn't work.\npygame.key.set_repeat(1,50) #Works with essentially no delay.", + "link" : "pygame.key.set_repeat", + "datetimeon" : "2010-03-02T22:20:03", + "id" : 3065 + }, + { + "datetimeon" : "2010-03-02T22:19:26", + "id" : 3064, + "user_title" : "David Khono Hackland", + "content" : "It appears that when the delay is set to zero, \nkey.set_repeat is returned to the default, disabled state.\nTo set it to a minimum, essentially no delay, just set it to 1.\n\npygame.key.set_repeat(0,50) #Doesn't work.\npygame.key.set_repeat(1,50) #Works with essentially no delay.", + "link" : "pygame.key.set_repeat" + }, + { + "link" : "Clock.tick", + "user_title" : "Mitchell K", + "content" : "Does it matter if you tick at the start or at the end?", + "id" : 2944, + "datetimeon" : "2009-09-12T21:09:15" + }, + { + "content" : "The example's .flip(..) below won't work - maybe I should have checked it before posting...\nHere is a better Version, it should work now.\n\nfrom pygame import Rect, Surface\nclass Sprites():\n def __init__(self, spritesheet, size):\n self.sheet = spritesheet\n self.sheet.convert_alpha()\n self.size = size\n \n self.sprites = []\n for x in xrange(spritesheet.get_width() / size[0]):\n list = []\n for y in xrange(spritesheet.get_height() / size[1]):\n list.append(spritesheet.subsurface(Rect((x*size[0], y*size[1]) , size)))\n self.sprites.append(list)\n def flip(self, xbool, ybool):\n new = Surface(self.sheet.get_size())\n new.fill((0, 0, 0, 0))\n for row in self.sprites:\n for sprite in row:\n new.blit(flip(sprite, xbool, ybool), sprite.get_offset())\n self.sheet.fill((0, 0, 0, 0))\n self.sheet.blit(new, (0, 0))\n def __getitem__(self, x=None, y=None):\n # not very tested, .flip(y=7) won't work\n # the if conditions should allow you to access a sheet with one row/col more easily .flip(5, 0) == .flip(5)\n if x is not None:\n if y is None:\n if len(self.sprites) > x:\n y = 0\n else:\n y = x\n x = 0\n elif y is None:\n raise IndexError\n \n return self.sprites[x][y]\n\n@any Developer/Moderator - it would be nice if my wrong post, \"The example below won't work\" and this Notice would be removed.", + "user_title" : "Rolf Sievers", + "link" : "Surface.subsurface", + "datetimeon" : "2009-11-06T11:31:59", + "id" : 2998 + }, + { + "link" : "Surface.subsurface", + "content" : "Here is a simple Sprite-sheet Class I wrote for an application, maybe someone can use it.\n\nfrom pygame import Rect\nclass Sprites():\n def __init__(self, spritesheet, size):\n self.sheet = spritesheet\n self.sheet.convert_alpha()\n self.size = size\n \n self.sprites = []\n for x in xrange(spritesheet.get_width() / size[0]):\n list = []\n for y in xrange(spritesheet.get_height() / size[1]):\n list.append(spritesheet.subsurface(Rect((x*size[0], y*size[1]) , size)))\n self.sprites.append(list)\n print list\n print self.sprites\n def flip(self, xbool, ybool):\n self.sheet.fill((0, 0, 0, 0))\n for row in self.sprites:\n for sprite in row:\n sprite.blit(flip(sprite, xbool, ybool), (0, 0))\n def __getitem__(self, x=None, y=None):\n if x is not None:\n if y is None:\n if len(self.sprites) > x:\n y = 0\n else:\n y = x\n x = 0\n elif y is None:\n raise IndexError\n \n return self.sprites[x][y]", + "user_title" : "Rolf Sievers", + "id" : 2997, + "datetimeon" : "2009-11-06T11:14:23" + }, + { + "user_title" : "Luke Endres", + "content" : "Is there anyway to get the rect of a polygon without having to create a surface greater than or equal to the polygon, and then gather the rect from the polygon?", + "link" : "pygame.draw.polygon", + "datetimeon" : "2009-08-08T21:50:46", + "id" : 2907 + }, + { + "user_title" : "Gummbum", + "content" : "This is twice the work because the image is rotated and then resized (subrect\nis copied) but it doesn't hurt my math-addled brain.\n\ndef rot_center(image, angle):\n \"\"\"rotate an image while keeping its center and size\"\"\"\n orig_rect = image.get_rect()\n rot_image = pygame.transform.rotate(image, angle)\n rot_rect = orig_rect.copy()\n rot_rect.center = rot_image.get_rect().center\n rot_image = rot_image.subsurface(rot_rect).copy()\n return rot_image", + "link" : "pygame.transform.rotate", + "datetimeon" : "2010-01-17T01:17:23", + "id" : 3034 + }, + { + "id" : 3035, + "datetimeon" : "2010-01-17T09:30:05", + "link" : "pygame.Surface", + "content" : "Use pygame.transform.rotate(Surface, angle)", + "user_title" : "Francesco Pasa" + }, + { + "link" : "pygame.display.toggle_fullscreen", + "user_title" : "Chris L", + "content" : "Does the returned Boolean value indicate success/failure at toggling fullscreen mode,\nor current status of the display (e.g., fullscreen = True, windowed = False)?", + "id" : 3041, + "datetimeon" : "2010-01-27T16:33:12" + }, + { + "id" : 2980, + "datetimeon" : "2009-10-23T11:07:21", + "link" : "pygame.event.set_blocked", + "content" : "set_blocked() clear queue from ALL events", + "user_title" : "ploutos" + }, + { + "id" : 2975, + "datetimeon" : "2009-10-21T19:51:47", + "link" : "pygame.cdrom", + "user_title" : "Jeffrey Aylesworth", + "content" : "If they play in a CD player, they were burned properly." + }, + { + "datetimeon" : "2009-09-24T16:05:59", + "id" : 2959, + "content" : "This is very important for mac because it shows the icon in the dock.. when I use this, it flashes the snake image for a second before changing, and it also gets smaller then the application icon which is the same (using py2app).", + "user_title" : "Mitchell K", + "link" : "pygame.display.set_icon" + }, + { + "id" : 3138, + "datetimeon" : "2010-06-08T15:33:04", + "link" : "pygame.time.get_ticks", + "user_title" : "Sergio Milardovich", + "content" : "\"\"\"\n\t # This is a get_ticks() function simple example\n\t # This script should return 10 as a result\n\"\"\"\n# Standard library imports\nimport time\n# Related third party imports\nimport pygame\n#Pygame start function\n\npygame.init()\n# Create the clock\nclock = pygame.time.Clock()\n# A simple loop of 10 stages\nfor i in range(10):\n\t# Update the clock\n\tclock.tick(1)\n# Print the seconds\nprint int(round(pygame.time.get_ticks()/1000))" + }, + { + "link" : "pygame.scrap", + "content" : "Note that pygame.scrap seems to be unimplemented in pygame-1.9.1.win32-py3.1.msi\n\nDefine testscrap.py, like this:\n\nimport pygame\npygame.init()\npygame.scrap.init()\n\n\nRun it, like this:\n\nC:\\Users\\Dave\\Documents\\Python>testscrap.py\nC:\\Users\\Dave\\Documents\\Python\\testscrap.py:3: RuntimeWarning: use scrap: No module named scrap\n(ImportError: No module named scrap)\n pygame.scrap.init()\nTraceback (most recent call last):\n File \"C:\\Users\\Dave\\Documents\\Python\\testscrap.py\", line 3, in \n pygame.scrap.init()\n File \"C:\\Python31\\lib\\site-packages\\pygame\\__init__.py\", line 70, in __getattr__\n raise NotImplementedError(MissingPygameModule)\nNotImplementedError: scrap module not available\n(ImportError: No module named scrap)", + "user_title" : "Dave Burton", + "id" : 3731, + "datetimeon" : "2011-01-16T00:15:21" + }, + { + "id" : 3732, + "datetimeon" : "2011-01-16T00:18:19", + "link" : "pygame.scrap", + "user_title" : "Dave Burton", + "content" : "BTW, the same error occurs if you have initialized a display surface, too:\n\nimport pygame\npygame.init()\nscreen=pygame.display.set_mode((640,360),0,32)\npygame.scrap.init()" + }, + { + "link" : "Surface.subsurface", + "user_title" : "Dave Burton", + "content" : "Hey, July 7 2009 Anonymous, that's a nice demo!\n\nFor Python 3 compatibility, just change the last line to:\n\n pygame.time.delay(1000//50)", + "id" : 3746, + "datetimeon" : "2011-01-24T10:00:06" + }, + { + "link" : "pygame.Rect", + "user_title" : "Dave Burton", + "content" : "Note that the order of the tuple members in virtual attributes like .topleft\nis always (x,y) [or (left,top) or (width,height)] even if the name of the\nvirtual attribute seems to suggest the opposite order. E.g.,\nrect1.topleft == (rect1.left,rect1.top)", + "id" : 3747, + "datetimeon" : "2011-01-25T01:19:14" + }, + { + "id" : 3750, + "datetimeon" : "2011-01-25T21:33:33", + "link" : "pygame.event", + "content" : "There's no 'code' member for type USEREVENT, unless you create one yourself\nwhen you create the event.", + "user_title" : "Dave Burton" + }, + { + "content" : "July 15 2010 Anonymous, here's your example of a resizeable pygame window.\n\nI don't know what you mean by \"window itself as well as the display.\"\nIf you want to resize something WITHIN the pygame window, just blit something\ndifferent onto it.\n\nDave\n\n\nimport sys, os, pygame\npygame.init()\n\nclock = pygame.time.Clock()\n\nscrsize = width,height = 600,400\nblack = 0,0,0\nbgcolor = (240,240,220) # light grey\n\n# to get the true full-screen size, do this BEFORE pygame.display.set_mode:\nfullscreen_sz = pygame.display.Info().current_w, pygame.display.Info().current_h\nprint( 'screen size =', fullscreen_sz )\n\n\n# ---------- This works under Windows Vista, no promises elsewhere! ----------\n# initially center the pygame window by setting %SDL_VIDEO_WINDOW_POS%\nwin_pos_left = 1 + ((fullscreen_sz[0] - width) // 2)\nwin_pos_top = 1 + ((fullscreen_sz[1] - height) // 2)\nos.environ['SDL_VIDEO_WINDOW_POS'] = '{0},{1}'.format(win_pos_left, win_pos_top)\n# ----------------------------------------------------------------------------\n\nscreen = pygame.display.set_mode(scrsize, pygame.RESIZABLE)\n\n# ----------------------------------------------------------------------------\nos.environ['SDL_VIDEO_WINDOW_POS'] = ''\n# if you don't clear the environment variable, the window will reposition\n# every time pygame.display.set_mode() gets called due to a VIDEORESIZE event.\n# ----------------------------------------------------------------------------\n\narial = pygame.font.SysFont( 'arial,microsoftsansserif,courier', 14 )\ntxt2display = arial.render( \"This window is resizeable\", True, black )\ntxt2display_w = txt2display.get_size()[0]\n\nwhile True:\n changed = False\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n pygame.quit()\n sys.exit(0)\n elif event.type == pygame.VIDEORESIZE:\n scrsize = event.size # or event.w, event.h\n screen = pygame.display.set_mode(scrsize,RESIZABLE)\n changed = True\n\n screen.fill( bgcolor )\n screen.blit( txt2display, ((scrsize[0]+1-txt2display_w)//2,1) ) # at top-center of screen\n pygame.display.update()\n if not changed:\n clock.tick(60) # limit to 60 fps", + "user_title" : "Dave Burton", + "link" : "pygame.display.init", + "datetimeon" : "2011-01-25T23:10:35", + "id" : 3751 + }, + { + "user_title" : "Dave Burton", + "content" : "Oops! Tiny correction... the 8th-to-last line should be\n\n screen = pygame.display.set_mode(scrsize,pygame.RESIZABLE)\n\n(Or else you can \"from pygame.locals import *\")", + "link" : "pygame.display.init", + "datetimeon" : "2011-01-25T23:16:00", + "id" : 3752 + }, + { + "user_title" : "Dave Burton", + "content" : "from pygame.locals import *\n\n_evnames = {} # from SDL-1.2.14\\include\\SDL_events.h\n_evnames[NOEVENT] = 'NOEVENT' # 0 SDL_NOEVENT\n_evnames[ACTIVEEVENT] = 'ACTIVEEVENT' # 1 SDL_ACTIVEEVENT\n_evnames[KEYDOWN] = 'KEYDOWN' # 2 SDL_KEYDOWN\n_evnames[KEYUP] = 'KEYUP' # 3 SDL_KEYUP\n_evnames[MOUSEMOTION] = 'MOUSEMOTION' # 4 SDL_MOUSEMOTION\n_evnames[MOUSEBUTTONDOWN] = 'MOUSEBUTTONDOWN' # 5 SDL_MOUSEBUTTONDOWN\n_evnames[MOUSEBUTTONUP] = 'MOUSEBUTTONUP' # 6 SDL_MOUSEBUTTONUP\n_evnames[JOYAXISMOTION] = 'JOYAXISMOTION' # 7 SDL_JOYAXISMOTION\n_evnames[JOYBALLMOTION] = 'JOYBALLMOTION' # 8 SDL_JOYBALLMOTION\n_evnames[JOYHATMOTION] = 'JOYHATMOTION' # 9 SDL_JOYHATMOTION\n_evnames[JOYBUTTONDOWN] = 'JOYBUTTONDOWN' # 10 SDL_JOYBUTTONDOWN\n_evnames[JOYBUTTONUP] = 'JOYBUTTONUP' # 11 SDL_JOYBUTTONUP\n_evnames[QUIT] = 'QUIT' # 12 SDL_QUIT\n_evnames[SYSWMEVENT] = 'SYSWMEVENT' # 13 SDL_SYSWMEVENT\n # 14 SDL_EVENT_RESERVEDA\n # 15 SDL_EVENT_RESERVEDB\n_evnames[VIDEORESIZE] = 'VIDEORESIZE' # 16 SDL_VIDEORESIZE\n_evnames[VIDEOEXPOSE] = 'VIDEOEXPOSE' # 17 SDL_VIDEOEXPOSE\n # 18 SDL_EVENT_RESERVED2\n # 19 SDL_EVENT_RESERVED3\n # 20 SDL_EVENT_RESERVED4\n # 21 SDL_EVENT_RESERVED5\n # 22 SDL_EVENT_RESERVED6\n # 23 SDL_EVENT_RESERVED7\n_evnames[USEREVENT] = 'USEREVENT' # 24 SDL_USEREVENT\n_evnames[NUMEVENTS] = 'NUMEVENTS' # 32 SDL_NUMEVENTS\n\n\ndef event_name(evtype):\n '''return a displayable name for a pygame/SDL event type number'''\n try:\n result = _evnames[evtype]\n except:\n if evtype in range(USEREVENT,NUMEVENTS):\n result = 'USEREVENT+' + repr(evtype-USEREVENT)\n elif evtype >= NUMEVENTS:\n result = 'ILLEGAL_EVENT_' + repr(evtype)\n elif evtype == 14:\n result = 'EVENT_RESERVEDA'\n elif evtype == 15:\n result = 'EVENT_RESERVEDB'\n else:\n result = 'EVENT_RESERVED' + repr(evtype-16)\n return result\n\n\nfor i in range(0,33):\n print(repr(i) + ' = ' + event_name(i))\n\n\n# It's all gonna change in SDL 1.3:\n#\n# SDL_FIRSTEVENT = 0 # Unused\n#\n# SDL_QUIT = 0x100 # User-requested quit\n#\n# SDL_WINDOWEVENT = 0x200 # Window state change\n# SDL_SYSWMEVENT = 0x201 # System specific event\n#\n# # Keyboard events\n# SDL_KEYDOWN = 0x300 # Key pressed\n# SDL_KEYUP = 0x301 # Key released\n# SDL_TEXTEDITING = 0x302 # Keyboard text editing (composition)\n# SDL_TEXTINPUT = 0x303 # Keyboard text input\n#\n# # Mouse events\n# SDL_MOUSEMOTION = 0x400 # Mouse moved\n# SDL_MOUSEBUTTONDOWN = 0x401 # Mouse button pressed\n# SDL_MOUSEBUTTONUP = 0x402 # Mouse button released\n# SDL_MOUSEWHEEL = 0x403 # Mouse wheel motion\n#\n# # Tablet or multiple mice input device events\n# SDL_INPUTMOTION = 0x500 # Input moved\n# SDL_INPUTBUTTONDOWN = 0x501 # Input button pressed\n# SDL_INPUTBUTTONUP = 0x502 # Input button released\n# SDL_INPUTWHEEL = 0x503 # Input wheel motion\n# SDL_INPUTPROXIMITYIN = 0x504 # Input pen entered proximity\n# SDL_INPUTPROXIMITYOUT = 0x505 # Input pen left proximity\n#\n# # Joystick events\n# SDL_JOYAXISMOTION = 0x600 # Joystick axis motion\n# SDL_JOYBALLMOTION = 0x601 # Joystick trackball motion\n# SDL_JOYHATMOTION = 0x602 # Joystick hat position change\n# SDL_JOYBUTTONDOWN = 0x603 # Joystick button pressed\n# SDL_JOYBUTTONUP = 0x604 # Joystick button released\n#\n# # Touch events\n# SDL_FINGERDOWN = 0x700\n# SDL_FINGERUP = 0x701\n# SDL_FINGERMOTION = 0x702\n# SDL_TOUCHBUTTONDOWN = 0x703\n# SDL_TOUCHBUTTONUP = 0x704\n#\n# # Gesture events\n# SDL_DOLLARGESTURE = 0x800\n# SDL_DOLLARRECORD = 0x801\n# SDL_MULTIGESTURE = 0x802\n#\n# # Clipboard events\n# SDL_CLIPBOARDUPDATE = 0x900 # The clipboard changed\n#\n# # Obsolete events\n# SDL_EVENT_COMPAT1 =0x7000 # SDL 1.2 events for compatibility\n# SDL_EVENT_COMPAT2 =0x7001\n# SDL_EVENT_COMPAT3 =0x7002\n#\n# # SDL_USEREVENT thru SDL_LASTEVENT are for your use\n# SDL_USEREVENT =0x8000\n# SDL_LASTEVENT =0xFFFF", + "link" : "pygame.event.Event", + "datetimeon" : "2011-01-27T04:08:06", + "id" : 3753 + }, + { + "id" : 3756, + "datetimeon" : "2011-01-27T18:49:33", + "link" : "PixelArray.compare", + "content" : "This function seems to me little bit buggy, so I wrote my own:\n\na and b are surfarrays of some surfaces that you want to compare\n\n def comparray(self,a,b):\n c = abs(a.__sub__(b))\n c = c.__ge__(self.tolerance)*255\n surface = pygame.surfarray.make_surface(c)\n return surface", + "user_title" : "Kaan Akßit" + }, + { + "user_title" : "Dave Burton", + "content" : "There's an error in this documentation w/r/t the final (width) argument:\n\n pygame.draw.rect(self.image, color, self.image.get_rect(), width=1)\nTypeError: rect() takes no keyword arguments\n\nLeave off the \"width=\" to make it work:\n\n pygame.draw.rect(self.image, color, self.image.get_rect(), 1)\n\nThis is with either pygame-1.9.1.win32-py2.6.msi or pygame-1.9.1.win32-py3.1.msi", + "link" : "pygame.draw.rect", + "datetimeon" : "2011-01-28T03:12:31", + "id" : 3757 + }, + { + "link" : "pygame.draw.line", + "content" : "There's an error in this documentation w/r/t the final (width) argument:\n\n pygame.draw.line(self.image, (0,0,0), (x,y), (x,y+h), width=2)\nTypeError: line() takes no keyword arguments\n\nLeave off the \"width=\" to make it work:\n\n pygame.draw.line(self.image, (0,0,0), (x,y), (x,y+h), 2)\n\nThis is with either pygame-1.9.1.win32-py2.6.msi or pygame-1.9.1.win32-py3.1.msi", + "user_title" : "Dave Burton", + "id" : 3759, + "datetimeon" : "2011-01-28T04:54:01" + }, + { + "id" : 3761, + "datetimeon" : "2011-01-29T20:46:36", + "link" : "Rect.collidepoint", + "content" : "Rect.center rounds UP:\n\nr0x0 = pygame.Rect(0,0,0,0) # a 0x0 rect\nprint('center of 0x0 rect is ' + repr(r0x0.center)) # result is (0,0) = not in the rect!\nr1x1 = pygame.Rect(0,0,1,1) # a 1x1 rect\nprint('center of 1x1 rect is ' + repr(r1x1.center)) # result is (0,0) = correct\nr2x2 = pygame.Rect(0,0,2,2) # a 2x2 rect\nprint('center of 2x2 rect is ' + repr(r2x2.center)) # result is (1,1) = rounded up!\nr3x3 = pygame.Rect(0,0,3,3) # a 3x3 rect\nprint('center of 3x3 rect is ' + repr(r3x3.center)) # result is (1,1) = exact\nr4x4 = pygame.Rect(0,0,4,4) # a 4x4 rect\nprint('center of 4x4 rect is ' + repr(r4x4.center)) # result is (2,2) = rounded up!\nr5x5 = pygame.Rect(0,0,5,5) # a 5x5 rect\nprint('center of 5x5 rect is ' + repr(r5x5.center)) # result is (2,2) = exact\nr6x6 = pygame.Rect(0,0,6,6) # a 6x6 rect\nprint('center of 6x6 rect is ' + repr(r6x6.center)) # result is (3,3) = rounded up!\nr7x7 = pygame.Rect(0,0,7,7) # a 7x7 rect\nprint('center of 7x7 rect is ' + repr(r7x7.center)) # result is (3,3) = exact", + "user_title" : "Dave Burton" + }, + { + "datetimeon" : "2011-01-29T20:47:58", + "id" : 3762, + "content" : "(Oops, I added that comment in the wrong place.)", + "user_title" : "Dave Burton", + "link" : "Rect.collidepoint" + }, + { + "id" : 3763, + "datetimeon" : "2011-01-29T20:48:50", + "link" : "pygame.Rect", + "user_title" : "Dave Burton", + "content" : "Rect.center rounds UP:\n\nr0x0 = pygame.Rect(0,0,0,0) # a 0x0 rect\nprint('center of 0x0 rect is ' + repr(r0x0.center)) # result is (0,0) = not in the rect!\nr1x1 = pygame.Rect(0,0,1,1) # a 1x1 rect\nprint('center of 1x1 rect is ' + repr(r1x1.center)) # result is (0,0) = correct\nr2x2 = pygame.Rect(0,0,2,2) # a 2x2 rect\nprint('center of 2x2 rect is ' + repr(r2x2.center)) # result is (1,1) = rounded up!\nr3x3 = pygame.Rect(0,0,3,3) # a 3x3 rect\nprint('center of 3x3 rect is ' + repr(r3x3.center)) # result is (1,1) = exact\nr4x4 = pygame.Rect(0,0,4,4) # a 4x4 rect\nprint('center of 4x4 rect is ' + repr(r4x4.center)) # result is (2,2) = rounded up!\nr5x5 = pygame.Rect(0,0,5,5) # a 5x5 rect\nprint('center of 5x5 rect is ' + repr(r5x5.center)) # result is (2,2) = exact\nr6x6 = pygame.Rect(0,0,6,6) # a 6x6 rect\nprint('center of 6x6 rect is ' + repr(r6x6.center)) # result is (3,3) = rounded up!\nr7x7 = pygame.Rect(0,0,7,7) # a 7x7 rect\nprint('center of 7x7 rect is ' + repr(r7x7.center)) # result is (3,3) = exact" + }, + { + "link" : "Rect.collidepoint", + "user_title" : "Dave Burton", + "content" : "This documentation is incorrect. A point along the right or bottom edge IS\nwithin the Rect, and points at coordinates on the bottom or right edge DO\ncollide with the Rect.\n\nHere's proof:\n\nr = Rect(0,0, 4,4) # a 4x4 rectangle\nprint('0,0: ' + repr(r.collidepoint(0,0)))\nprint('1,1: ' + repr(r.collidepoint(1,1)))\nprint('2,2: ' + repr(r.collidepoint(2,2)))\nprint('3,3: ' + repr(r.collidepoint(3,3)))\nprint('4,4: ' + repr(r.collidepoint(4,4)))\n\nWith pygame 1.9.1 under both Python 3.1 and 2.6, it prints:\n\n0,0: 1\n1,1: 1\n2,2: 1\n3,3: 1\n4,4: 0\n\nNote that the bottom-right pixel within the 4x4 rect is at (3,3) and\ncollidepoint((3,3)) does return 1 (meaning true).\n\nA second (minor) documentation error is that it actually returns an integer\n1 or 0 instead of boolean True or False.", + "id" : 3764, + "datetimeon" : "2011-01-29T22:35:18" + }, + { + "id" : 3774, + "datetimeon" : "2011-02-07T04:48:40", + "link" : "pygame.event", + "user_title" : "Dave Burton", + "content" : "This class is a bit odd. Event objects have no event.__dict__ attribute,\nand the dir(event) function doesn't work. However, repr(event) returns a\nnice, thorough description of an event object and its attributes, and the\nevent.dict attribute lists all the important attributes except .type and\n.dict itself." + }, + { + "user_title" : "Dave Burton", + "content" : "With a Microsoft IntelliMouse p/n X05-77975, under Windows Vista,\nwith either Python 2.6 or 3.1, the button numbers are:\n1 = left button\n2 = center button/wheel press\n3 = right button\n4 = wheel roll forward/up\n5 = wheel roll backward/down\n6 = left side extra button\n7 = right side extra button", + "link" : "pygame.event", + "datetimeon" : "2011-02-23T21:04:46", + "id" : 3794 + }, + { + "link" : "pygame.cursors", + "user_title" : "Dave Burton", + "content" : "There's a cursor missing! sizer_xy_strings defines an upper-left-to-lower-right\nresizer cursor, suitable for dragging the upper-left or lower-right corner.\nBut there's no sizer_yx_strings to make the upper-right-to-lower-left cursor.\nHere's how I made one:\n\nsizer_yx_strings = [ x[12::-1]+x[13:] for x in pygame.cursors.sizer_xy_strings ]", + "id" : 3795, + "datetimeon" : "2011-02-24T02:08:27" + }, + { + "id" : 3796, + "datetimeon" : "2011-02-24T02:13:59", + "link" : "pygame.cursors", + "content" : "Or, equivalently:\n\nsizer_yx_strings = ( #sized 24x16\n \" XXXXXXXX \",\n \" X.....X \",\n \" X....X \",\n \" X...X \",\n \" X.X..X \",\n \" X.X X.X \",\n \"X X.X XX \",\n \"XX X.X X \",\n \"X.XX.X \",\n \"X...X \",\n \"X...X \",\n \"X....X \",\n \"X.....X \",\n \"XXXXXXXX \",\n \" \",\n \" \",\n)", + "user_title" : "Dave Burton" + }, + { + "id" : 3799, + "datetimeon" : "2011-03-01T13:26:33", + "link" : "pygame.mixer.Sound", + "content" : "The Sound function now accepts 'buffer', 'file', and 'array' keyword arguments\nto remove any ambiguity in how to treat an argument. The 'array' keyword is new,\nand tells Sound to look check the argument for an array struct interface or\nthe new buffer protocol if supported. This allows Sound to function like\nsndarray.make_sound.\n\nSound also exposes an array struct interface and the new buffer protocol.", + "user_title" : "Lenard Lindstrom" + }, + { + "content" : "I'm not sure which version of Pygame is being used here, 1.9? At the time it was\nreleased NumPy was unavailable for Python 3.1. Python 1.9.2 alpha from SVN\ncertainly does support NumPy for Python 3.1, and 3.2.", + "user_title" : "Lenard Lindstrom", + "link" : "pygame.surfarray", + "datetimeon" : "2011-03-01T13:35:12", + "id" : 3800 + }, + { + "id" : 3801, + "datetimeon" : "2011-03-01T13:37:12", + "link" : "pygame.surfarray", + "user_title" : "Lenard Lindstrom", + "content" : "That should be \"which version of Pygame is being used here, 1.9.1?\"" + }, + { + "id" : 3802, + "datetimeon" : "2011-03-01T13:38:40", + "link" : "pygame.surfarray.pixels_alpha", + "user_title" : "Lenard Lindstrom", + "content" : "New to Pygame 1.9.2 for NumPy: pixels_red, pixels_green, and pixels_blue." + }, + { + "user_title" : "Daniel Kaminsky", + "content" : "For the KEYDOWN and KEYUP event \"scancode\" is also a member and can be used \nfor the unknown keys", + "link" : "pygame.event", + "datetimeon" : "2011-03-23T05:51:58", + "id" : 3872 + }, + { + "link" : "pygame.mouse", + "content" : "The wheel generates pygame.MOUSEBUTTONUP events too, not just pygame.MOUSEBUTTONDOWN event.", + "user_title" : "Dan Ross", + "id" : 3884, + "datetimeon" : "2011-04-02T23:30:45" + }, + { + "datetimeon" : "2011-04-03T18:28:23", + "id" : 3885, + "content" : "Forget what the functions do, check out Mr. Brown's naming style. Its pure genius!\n1) angle_times_WOW_pi_divided_by_180\n2) HE_HE_strange_popper_z\n3) buffy_the_fat2\n4) they_did_touch\n5) while Grr < LIN_collide_max:\n6) Rotated_Relate_ball1_z__PLUS__Rotated_ball1_zol\n7) write_to_file_WEEE_STRANGE()\n8) freaky_rect_switcharoo_2D()", + "user_title" : "Mad Cloud Games", + "link" : "pygame.draw.circle" + }, + { + "link" : "pygame.transform.chop", + "content" : "If you like to receive the inner rectangle, the blit is a much better setup. \nThe following comparing examples show how-to cut a (centered) 150x150 frame out of a 250x250 image:\norig_surf = pygame.Surface((250,250),flags=pygame.SRCALPHA)\npygame.draw.circle(orig_surf,(255,0,0),(50,50),25)\npygame.draw.circle(orig_surf,(0,255,0),(50,200),25)\npygame.draw.circle(orig_surf,(0,0,255),(200,50),25)\npygame.draw.circle(orig_surf,(0,255,255),(200,200),25)\n\ncrop_surf = pygame.transform.chop(pygame.transform.chop(orig_surf,(0,0,50,50)),(150,150,250,250))\npygame.image.save(crop_surf, 'test-crop.png')\n\n\ncrop_surf = pygame.Surface((150,150),flags=pygame.SRCALPHA)\ncrop_surf.blit(orig_surf, (0,0),(50,50,200,200))\npygame.image.save(crop_surf, 'test-blit.png')", + "user_title" : "Rick van der Zwet", + "id" : 4045, + "datetimeon" : "2011-05-05T04:36:44" + }, + { + "user_title" : "Anonymous", + "content" : "It all seemed simple and working properly, then I noticed... \"The area covered by a Rect does not include the right- and bottom-most edge of pixels. If one Rect's bottom border is another Rect's top border (i.e., rect1.bottom=rect2.top), the two meet exactly on the screen but do not overlap, and rect1.colliderect(rect2) returns false.\"\n\n*mutter* good to know.", + "link" : "Rect.colliderect", + "datetimeon" : "2011-01-10T19:28:58", + "id" : 3725 + }, + { + "user_title" : "Anonymous", + "content" : "Note that when the user resizes the game window, pygame does not automatically update its internal screen surface. You must call set_mode() every time VIDEORESIZE is sent. This really should be more clear in the documentation.", + "link" : "pygame.display", + "datetimeon" : "2011-01-11T15:55:57", + "id" : 3726 + }, + { + "datetimeon" : "2011-01-13T08:28:22", + "id" : 3727, + "content" : "Is it possible to set this mode transparent?\nI mean without changing the transparency with set_alpha or ... but from the beginning.", + "user_title" : "Anonymous", + "link" : "pygame.display.set_mode" + }, + { + "link" : "pygame.draw.aaline", + "content" : "Draw a normal thick line, then draw two aa lines either side. Not exactly what you want but it will work.", + "user_title" : "Anonymous", + "id" : 3728, + "datetimeon" : "2011-01-13T15:22:43" + }, + { + "link" : "pygame.draw.line", + "content" : "This code fixes the bad rect given by the line function.\n\ntemprect=(pygame.draw.line(screen,color,firstpos,newpos,thick))\ntemprect.inflate_ip(thick*2, thick*2)\ndirty.append(temprect)", + "user_title" : "Anonymous", + "id" : 8, + "datetimeon" : "2005-11-22T22:22:44" + }, + { + "id" : 13, + "datetimeon" : "2005-11-27T22:45:10", + "link" : "pygame.draw.ellipse", + "user_title" : "Anonymous", + "content" : "if your rect contains a negative width or height you need to rect.normalize() your rect before passing it to this function" + }, + { + "user_title" : "Anonymous", + "content" : "Rotates image about its center.", + "link" : "pygame.transform.rotate", + "datetimeon" : "2005-11-28T19:22:44", + "id" : 14 + }, + { + "link" : "pygame.transform.rotate", + "content" : "Make sure you blit according to the center of the newly formed surface, and not what the center of the orginal image is.", + "user_title" : "Anonymous", + "id" : 15, + "datetimeon" : "2005-11-28T19:24:48" + }, + { + "user_title" : "Anonymous", + "content" : "This probably goes without saying, but always rotate the orginal image, not a rotated copy.", + "link" : "pygame.transform.rotate", + "datetimeon" : "2005-11-28T19:26:45", + "id" : 16 + }, + { + "datetimeon" : "2005-12-01T10:30:49", + "id" : 18, + "user_title" : "Anonymous", + "content" : "Before calling pygame.key.get_pressed(), one should call pygame.event.pump() to get the lates state of the keyboard.\n\nThis is so because the get_pressed() function wraps the SDL_GetKeyState() function and in the SDL_GetKeyState() documentation it is written that one should use SDL_PumpEvents() to update the state array and pygame.event.pump() just happens to be a wrapper for SDL_PumpEvents() :-)", + "link" : "pygame.key.get_pressed" + }, + { + "user_title" : "Anonymous", + "content" : "When I tryed to use this, he couldn't find the key K_t I wanted\n untill I used:\n\nfrom pygame.locals import *\n\nSo be sure to use it - Shefy", + "link" : "pygame.key.get_pressed", + "datetimeon" : "2005-12-07T04:09:33", + "id" : 19 + }, + { + "datetimeon" : "2005-12-10T19:21:13", + "id" : 22, + "user_title" : "Anonymous", + "content" : "if you pass in None as the background argument, you get the error\n\"TypeError: Invalid background RGBA argument\"", + "link" : "Font.render" + }, + { + "id" : 32, + "datetimeon" : "2005-12-25T19:36:47", + "link" : "pygame.key.get_pressed", + "content" : "pygame.event.pump()\n m = pygame.key.get_mods()\n if m & KMOD_SHIFT:\n print 'shift pressed'", + "user_title" : "Anonymous" + }, + { + "link" : "pygame.transform.rotate", + "content" : "Rotated objects tend to move around because bounding rectangle changes size.\nStore the center in a temporary variable, then rotate the original image, and finally reset the center before you blit or update\nThis code comes from a sprite class:\n\n def turn(self, amount):\n \"turn some amount\"\n oldCenter = self.rect.center\n self.dir += amount\n self.image = pygame.transform.rotate(self.baseImage, self.dir)\n self.rect = self.image.get_rect()\n self.rect.center = oldCenter", + "user_title" : "Anonymous", + "id" : 36, + "datetimeon" : "2006-01-03T09:48:09" + }, + { + "id" : 37, + "datetimeon" : "2006-01-04T09:16:25", + "link" : "pygame.mouse.get_pressed", + "content" : "This effect (1 + 3 = 2) is caused by your X.org/XServer mouse configuration section, which allows to emulate the middle button by clicking both the left and right mouse button at the same time.", + "user_title" : "Anonymous" + }, + { + "datetimeon" : "2006-01-17T14:45:02", + "id" : 41, + "content" : "This does not result in 'truly' transparent text, as the area between the letters is filled in with the background color. For truly transparent text with an invisible background behind the letters, use Numeric:\n\ndef RenderTransparent(font, text, antialias=1, color=(255, 0, 0, 0)):\n 'Render text with transparency underneath the letters'\n 'Requires Numeric'\n\n # Create a colored block big enough to hold the text\n w, h = font.size(text)\n surface = pygame.Surface((w, h), pygame.SRCALPHA)\n surface.fill(color)\n \n # Create an alpha channel that contains the shapes of the letters\n alpha = pygame.Surface((w, h), pygame.SRCALPHA)\n WHITE = (255, 255, 255, 0)\n BLACK = (0, 0, 0, 0)\n a = font.render(text, antialias, WHITE, BLACK)\n alpha.blit(a, (0, 0))\n \n # Combine the alpha channel with the colored block\n pic = surface.convert_alpha()\n mask = alpha.convert(32)\n mskarray = pygame.surfarray.pixels3d(mask)\n pygame.surfarray.pixels_alpha(pic)[:, :] = mskarray[:, :, 0]\n\n # Return the 'truly' transparent text.\n return pic", + "user_title" : "Anonymous", + "link" : "Font.render" + }, + { + "user_title" : "Anonymous", + "content" : "LOL", + "link" : "PixelArray.replace", + "datetimeon" : "2011-01-03T19:03:40", + "id" : 3700 + }, + { + "datetimeon" : "2011-01-03T19:05:09", + "id" : 3701, + "content" : "FUCKING SPAMMER MOTHER FUCKERS WHO OWNS THIS SHIT THEY SHOULD BURN IN HELLL", + "user_title" : "Anonymous", + "link" : "pygame.locals" + }, + { + "link" : "pygame.cursors.compile", + "content" : "On my Windows Vista machine running Python 3.1.2 and pygame 1.9.1,\nthe 'black=' and 'white=' parameters are swapped.\n\nSo, to make the example work (with a black arrow outline\naround a white center), you have to do this:\n\nthickarrow_strings = ( #sized 24x24\n \"XX \",\n \"XXX \",\n \"XXXX \",\n \"XX.XX \",\n \"XX..XX \",\n \"XX...XX \",\n \"XX....XX \",\n \"XX.....XX \",\n \"XX......XX \",\n \"XX.......XX \",\n \"XX........XX \",\n \"XX........XXX \",\n \"XX......XXXXX \",\n \"XX.XXX..XX \",\n \"XXXX XX..XX \",\n \"XX XX..XX \",\n \" XX..XX \",\n \" XX..XX \",\n \" XX..XX \",\n \" XXXX \",\n \" XX \",\n \" \",\n \" \",\n \" \")\n\ndatatuple, masktuple = pygame.cursor.compile( thickarrow_strings,\n black='.', white='X', xor='o' )\npygame.mouse.set_cursor( (24,24), (0,0), datatuple, masktuple )", + "user_title" : "Anonymous", + "id" : 3702, + "datetimeon" : "2011-01-04T09:45:11" + }, + { + "id" : 49, + "datetimeon" : "2006-01-29T16:18:15", + "link" : "pygame.mixer.Channel", + "content" : "I'm using this generator to get a channel id for each sprite:\n\ndef free_sound_channel():\n \"\"\"Get next available sound channel\n Usage:\n free_channels=free_sound_channel()\n id=free_channels.next()\n \"\"\"\n id=0\n while id<pygame.mixer.get_num_channels():\n yield id\n id+=1\n return # or: raise StopIteration()", + "user_title" : "Anonymous" + }, + { + "id" : 52, + "datetimeon" : "2006-02-07T21:37:24", + "link" : "Surface.get_flags", + "content" : "COLORKEY and ALPHA should have 'SRC' prefixed to them. Here is a more-complete list of flags revelvant to surface.get_flags():\n,\"SRCCOLORKEY\"\n,\"RLEACCEL\"\n,\"RLEACCELOK\"\n,\"PREALLOC\"\n,\"HWACCEL\"\n,\"SRCALPHA\"\n,\"UYVY_OVERLAY\"\n,\"YV12_OVERLAY\"\n,\"YVYU_OVERLAY\"\n,\"YUY2_OVERLAY\"\n,\"HWPALETTE\"\nSWSURFACE - not really usable as a surface flag, equates to 0 and is always default\nANYFORMAT - used to create surfaces, pygame defaults to this flag if you don't specifya bit depth\nHWACCEL - surface is hardware accelerated, readonly\nSRCCOLORKEY- surface has a colorkey for blits, readonly\nSRCALPHA - surface has alpha enabled, readonly\nRLEACCELOK - surface is rle accelerated, but hasn't been compiled yet, readonly\nPREALLOC - not even sure?\nHope this helps....", + "user_title" : "Anonymous" + }, + { + "datetimeon" : "2006-02-07T22:02:57", + "id" : 53, + "content" : "I wish all the possible flags were documented here...", + "user_title" : "Anonymous", + "link" : "pygame.Surface" + }, + { + "id" : 54, + "datetimeon" : "2006-02-08T02:29:13", + "link" : "Font.render", + "user_title" : "Anonymous", + "content" : "I don't know what is wrong with you two. I tested the following and it worked as expected. Perhaps it is because I tested it on windows, if you tested it somewhere else (of course that's not the likely cause but I really can't see what else is wrong).\n\nIt is true that passing None for the final argument causes \"Invalid RGBA argument\". This is a bug in the documentation, not the code. The proper way to get transparency is to simply omit the last argument.\n\n$python\n>>>import pygame\n>>>pygame.init()\n>>>screen = pygame.display.set_mode((300,300))\n>>>screen.fill((255,0,0))\n>>>pygame.display.flip()\n\n>>>font = pygame.font.SysFont(\"Times New Roman\",30)\n>>>s = font.render(\"Eggs are good for you, but not on the eiffel tower\",True,(0,255,255))\n>>>s.get_flags() #-> 65536 [SRCALPHA].. good, implies the image has per-pixel transparency\n>>>[s.get_at((i,j)) for i in range(20) for j in range(20)]\n[.... #here we see that indeed each\n(0,255,255,68) #pixel is a full RGBA pixel with 4\n....] #components.\n>>>screen.blit(s, (0,0))\n>>>pygame.display.flip()\n>>>pygame.event.pump() #in order to bring the window back to life...\n\nAnd the result is turquoise text with red in the background, clearly showing transparency. Phew, you had me worried there, thinking I couldn't do transparency with this... until I looked closer. These docs are shiny but can be very hard to read sometimes." + }, + { + "datetimeon" : "2006-02-21T15:49:19", + "id" : 58, + "user_title" : "Anonymous", + "content" : "Here's another solution for creating surfaces with per-pixel-alpha:\n\nimage = pygame.Surface((width,height),pygame.SRCALPHA,32);\n\nAdding the depth argument '32' seems to make this work every time.", + "link" : "pygame.Surface" + }, + { + "user_title" : "Anonymous", + "content" : "In the documentery it says \"The antialias argument is a boolean, if true the \ncharacters will have smooth edges.\". If you pass a string as the antialias \nargument it raises an exception saying \"TypeError: an integer is required\". This\nis very confusing. It should raise \"TypeError: a boolean is required\". \nIf antialias is enabled it will greatly drop the framerate (from 100 to 33 on my\nmachine). Font.render should be called only for as many times as you need fonts.\nDo not call this function every gameloop for it will greatly drop the framerate.\n(this cost me about 2 houres of debugging to find out.)\nIf any admins read this: Please change the script so that long lines will be seperated to shorter lines. Those 500+ words lines are uncomfortable to read with all that scrolling. mfg nwp.", + "link" : "Font.render", + "datetimeon" : "2006-03-05T13:28:14", + "id" : 61 + }, + { + "id" : 64, + "datetimeon" : "2006-03-08T22:55:41", + "link" : "pygame.init", + "user_title" : "Anonymous", + "content" : "you may want to initalise the \ndifferent modules seperately\nto speed up your program. Of \ncourse, then you would need \nto know which modules you have\ninitalised and which ones you\nhave not." + }, + { + "link" : "Hope to find some more useful information on your site! It is really great!", + "user_title" : "Anonymous", + "content" : "", + "id" : 196, + "datetimeon" : "2006-12-30T07:59:42" + }, + { + "link" : "pygame.cdrom", + "content" : "format of music files\non cds are (usualy) in\nCD Digital Audio, except\nsometimes a program will\nmake a cd useing a \ndifferent format, so \npygame.cdrom.CD(n).play()\nwill maby not play it.", + "user_title" : "Anonymous", + "id" : 67, + "datetimeon" : "2006-03-08T23:02:35" + }, + { + "id" : 82, + "datetimeon" : "2006-04-02T00:38:08", + "link" : "pygame.mouse.get_pressed", + "content" : "import pygame\nfrom pygame.locals import *\n\npygame.init()\npygame.display.set_mode((300,200))\npygame.display.set_caption('Mouse Input Demonstration')\nrunning = True\nwhile running:\n for event in pygame.event.get():\n if event.type == QUIT:\n running = False\n if event.type == KEYDOWN and event.key == K_ESCAPE:\n running = False\n if event.type == MOUSEBUTTONDOWN:\n print event.button\n\npygame.display.quit()", + "user_title" : "Anonymous" + }, + { + "user_title" : "Anonymous", + "content" : "# An Example from perldude69@gmail.com www.wachadoo.com/forum/\n# CONSTANTS\nSCREEN_WIDTH = 800\nSCREEN_HEIGHT = 600\n#Initialise Game\npygame.init()\nscreen = pygame.display.set_mode( (SCREEN_WIDTH,SCREEN_HEIGHT))\npygame.display.set_caption('Space Invaders')\nbackground = pygame.image.load('./pics/background1.jpg').convert()\nbackground = pygame.transform.scale(background,( SCREEN_WIDTH, SCREEN_HEIGHT))\nscreen.blit(background, (0,0)) \npygame.display.flip() \ndone = False\nwhile not done:\n\tfor e in pygame.event.get():\n\t\tif e.type == KEYDOWN:\n\t\t\tdone = True\nif __name__ == \"__main__\":\n main()", + "link" : "pygame.transform.scale", + "datetimeon" : "2006-04-07T18:04:11", + "id" : 84 + }, + { + "datetimeon" : "2006-06-13T21:27:25", + "id" : 98, + "user_title" : "Anonymous", + "content" : "Don't specify flags unless you absolutely *must* (that is, don't specify HWSURFACE, depth=32 just because you think it's a good idea). This will reduce the portability of your game.", + "link" : "pygame.display.set_mode" + }, + { + "id" : 183, + "datetimeon" : "2006-12-18T16:29:25", + "link" : "pygame.key.get_mods", + "content" : "Could someone please post the integer values corresponding to the various shift/ctl/alt keys? Or provide a link.\nthank you!", + "user_title" : "Anonymous" + }, + { + "link" : "pygame.surfarray", + "user_title" : "Anonymous", + "content" : "numpy is fine in Python 3.1.2. However, pygame.surfarray doesn't work\nat all in pygame-1.9.1.win32-py3.1.msi with python-3.1.2.msi and\nnumpy-1.5.1-win32-superpack-python3.1.exe under Windows Vista.\n\nTo see the problem, just run the test that comes with it; 4 of 14 tests fail:\n\nC:\\>cd \\python31\\lib\\site-packages\\pygame\\tests\n\nC:\\Python31\\Lib\\site-packages\\pygame\\tests>\\python31\\python surfarray_test.py\nEE.EE.........\n======================================================================\nERROR: test_array2d (__main__.SurfarrayModuleTest)\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"surfarray_test.py\", line 147, in test_array2d\n arr = pygame.surfarray.array2d(surf)\n File \"C:\\python31\\lib\\site-packages\\pygame\\surfarray.py\", line 104, in array2d\n return numpysf.array2d (surface)\n File \"C:\\python31\\lib\\site-packages\\pygame\\_numpysurfarray.py\", line 77, in array2d\n data = ''.join (pattern.findall (data))\nTypeError: can't use a string pattern on a bytes-like object\n[...snip...]", + "id" : 3704, + "datetimeon" : "2011-01-07T03:47:28" + }, + { + "link" : "pygame.draw.arc", + "content" : "no fill?", + "user_title" : "Anonymous", + "id" : 163, + "datetimeon" : "2006-11-19T14:38:36" + }, + { + "link" : "pygame.PixelArray", + "user_title" : "Anonymous", + "content" : "works perfectly fine for me... question: what's the name of the overloaded operator that does the pxarray[x,y] subscripting?", + "id" : 3689, + "datetimeon" : "2010-12-23T18:28:10" + }, + { + "link" : "pygame.cursors", + "content" : "pygame.cursors.ball is also a cool one.", + "user_title" : "Anonymous", + "id" : 3690, + "datetimeon" : "2010-12-28T16:21:48" + }, + { + "datetimeon" : "2010-12-30T05:47:55", + "id" : 3691, + "user_title" : "Anonymous", + "content" : "Its a success/failure scenario. It returns True (1) if it went well.", + "link" : "pygame.display.toggle_fullscreen" + }, + { + "id" : 3694, + "datetimeon" : "2011-01-01T15:04:01", + "link" : "pygame.gfxdraw.pie", + "content" : "These appear to be in degrees rather than radians (different than how draw.arc()'s are specified) which is kind of inconsistent. Are these documented better elsewhere?", + "user_title" : "Anonymous" + }, + { + "link" : "pygame.display", + "user_title" : "Anonymous", + "content" : "You need to put\nimport pygame\nat the top of your program, anonymous.", + "id" : 3695, + "datetimeon" : "2011-01-02T07:03:47" + }, + { + "datetimeon" : "2006-12-28T17:27:45", + "id" : 188, + "user_title" : "Anonymous", + "content" : "", + "link" : "Looking for information and found it at this great site..." + }, + { + "user_title" : "Anonymous", + "content" : "", + "link" : "I love the whiiite suits! Great show!", + "datetimeon" : "2006-12-29T02:41:08", + "id" : 189 + }, + { + "datetimeon" : "2006-12-29T10:09:22", + "id" : 190, + "content" : "", + "user_title" : "Anonymous", + "link" : "Thank you for your site. I have found here much useful information..." + }, + { + "datetimeon" : "2010-12-13T21:22:42", + "id" : 3685, + "content" : "I had this weird thing where blue/red was inversed, but not the other colours, when I was mapping some pixels from one image to a blank surface.\nIt was caused by copying the color integer directly to one pixel to the other, so the trick is to always surface.unmap_rgb(pixel) before setting the color to a new pixel", + "user_title" : "Anonymous", + "link" : "pygame.PixelArray" + }, + { + "user_title" : "Anonymous", + "content" : ".", + "link" : "Movie.play", + "datetimeon" : "2010-12-17T14:01:47", + "id" : 3687 + }, + { + "user_title" : "Anonymous", + "content" : "import pygame, sys\nfrom pygame.version import ver\nprint (\"pygame \", ver)\nstartstate = pygame.init()\nprint (\"{pygame.init()}\", startstate)\nscreen = pygame.display.set_mode([640, 480])\nprint (\"{pygame.display.set_mode([640, 480]}\", screen)\nwhile True:\n for event in pygame.event.get():\n if not event:\n print (\"Event processing error: cannot find event.\")\n elif event.type == pygame.QUIT or event.type == pygame.K_ESCAPE:\n print (\"{for event in pygame.event.get():} : \", event)\n sys.exit()\nsys.exit() command does not run when I press escape, all it does is the same as if not event.", + "link" : "pygame.key", + "datetimeon" : "2010-12-18T17:09:49", + "id" : 3688 + }, + { + "content" : "It the range for H should only be [0, 360); at exactly 360 the expression throws an OverflowError. The other ranges are not affected as such.", + "user_title" : "Anonymous", + "link" : "Color.hsva", + "datetimeon" : "2010-12-08T17:55:35", + "id" : 3677 + }, + { + "user_title" : "Anonymous", + "content" : "", + "link" : "Very cool design! Useful information. Go on!", + "datetimeon" : "2006-12-30T22:35:22", + "id" : 202 + }, + { + "datetimeon" : "2006-12-31T05:52:59", + "id" : 203, + "content" : "", + "user_title" : "Anonymous", + "link" : "Very interesting! site. A must bookmark! I wait for continuation" + }, + { + "datetimeon" : "2006-12-31T13:03:40", + "id" : 204, + "content" : "", + "user_title" : "Anonymous", + "link" : "Very interesting! site. A must bookmark! I wait for continuation" + }, + { + "link" : "Just wanted to say you have some happyY looking walkers. All natural!", + "content" : "", + "user_title" : "Anonymous", + "id" : 205, + "datetimeon" : "2006-12-31T19:55:09" + }, + { + "id" : 208, + "datetimeon" : "2007-01-01T16:59:55", + "link" : "You have an outstanding good and well structured site. I enjoyed browsing through it.", + "user_title" : "Anonymous", + "content" : "" + }, + { + "datetimeon" : "2007-01-02T00:19:19", + "id" : 211, + "user_title" : "Anonymous", + "content" : "", + "link" : "I love the whiiite suits! Great show!" + }, + { + "link" : "Hope to find some more useful information on your site! It is really great!", + "user_title" : "Anonymous", + "content" : "", + "id" : 212, + "datetimeon" : "2007-01-02T15:12:24" + }, + { + "id" : 213, + "datetimeon" : "2007-01-02T22:17:19", + "link" : "Just wanted to say you have some happyY looking walkers. All natural!", + "user_title" : "Anonymous", + "content" : "" + }, + { + "link" : "You have a great site. All in your web is very useful. Please keep on working.", + "user_title" : "Anonymous", + "content" : "", + "id" : 214, + "datetimeon" : "2007-01-03T05:35:22" + }, + { + "id" : 215, + "datetimeon" : "2007-01-03T12:53:55", + "link" : "Pretty nice site, wants to see much more on it! :)", + "content" : "", + "user_title" : "Anonymous" + }, + { + "link" : "You have an outstanding good and well structured site. I enjoyed browsing through it.", + "content" : "", + "user_title" : "Anonymous", + "id" : 216, + "datetimeon" : "2007-01-03T20:02:38" + }, + { + "link" : "This site is asomeee, well done, thanks for all!", + "content" : "", + "user_title" : "Anonymous", + "id" : 218, + "datetimeon" : "2007-01-04T18:10:07" + }, + { + "user_title" : "Anonymous", + "content" : "", + "link" : "Very nice site. Keep up the great work.", + "datetimeon" : "2007-01-05T01:15:13", + "id" : 219 + }, + { + "datetimeon" : "2007-01-29T17:22:04", + "id" : 316, + "user_title" : "Anonymous", + "content" : "Left:\nctrl 4160 \nshift 4097 \nalt 4352 \n\nRight:\nctrl 4224\nshift 4098\nalt 4608\n\nLeft:\nc+s 4161\nc+a 4416\na+s 4353\n\nRight:\nc+s 4226\nc+a 4736\na+s 4610\n\nDone by hand ;)\n\n-Jabapyth", + "link" : "pygame.key.get_mods" + }, + { + "datetimeon" : "2007-02-08T19:53:05", + "id" : 335, + "user_title" : "Anonymous", + "content" : "Depending on your keyboard there may be limitations of how many simultaneous keypresses can be detected by this command. Some combinations will work on one keyboard and not on another.", + "link" : "pygame.key.get_pressed" + }, + { + "datetimeon" : "2007-02-26T18:27:17", + "id" : 374, + "content" : "Just use the same line width as your radius.\nThis of course dosn't solve your problem if you want a border on your arc, but then you can just paint twice.", + "user_title" : "Anonymous", + "link" : "pygame.draw.arc" + }, + { + "link" : "pygame.draw.line", + "content" : "It looks like width is not a keyword argument, but a required/positional/whatever one instead.\n\n>>> pygame.draw.line(surf, color, (x1, y1), (x2, y2), width=width)\nTraceback (most recent call last):\n File \"\", line 1, in ?\nTypeError: line() takes no keyword arguments\n>>> pygame.draw.line(surf, color, (x1, y1), (x2, y2))\n\n>>> pygame.draw.line(surf, color, (x1, y1), (x2, y2), 1)", + "user_title" : "Anonymous", + "id" : 386, + "datetimeon" : "2007-03-01T11:28:42" + }, + { + "datetimeon" : "2007-03-03T16:39:29", + "id" : 400, + "content" : "first number = top left rectangle x coordinate\nsecond number = top left rectangle y coordinate\nthird number = width of rectangle\nfourth number = length of rectangle", + "user_title" : "Anonymous", + "link" : "pygame.draw.rect" + }, + { + "link" : "Rect.move", + "user_title" : "Anonymous", + "content" : "If you want to make a deep copy of a Rect object (without importing the copy module)\nthen you can do so by calling move with the arguments (0,0).", + "id" : 1359, + "datetimeon" : "2008-01-04T00:22:04" + }, + { + "datetimeon" : "2007-03-11T20:32:13", + "id" : 434, + "user_title" : "Anonymous", + "content" : "the forth numer is the height of the rect", + "link" : "pygame.draw.rect" + }, + { + "link" : "pygame.mixer.music.play", + "content" : "The documentation is incorrect. pygame.mixer.music(5) will indeed play the music five times, not six. Perhaps the function used to behave differently, but I can find nothing in the documentation for either pygame or SDL_mixer that suggests so.", + "user_title" : "Anonymous", + "id" : 440, + "datetimeon" : "2007-03-19T15:33:19" + }, + { + "datetimeon" : "2007-03-19T15:34:19", + "id" : 441, + "content" : "I meant to say pygame.mixer.music.play(5), of course. I left out the \"play\" part.", + "user_title" : "Anonymous", + "link" : "pygame.mixer.music.play" + }, + { + "content" : "Properties in the object returned by get_rect():\n\nbottom\nbottomleft\nbottomright\ncenter\ncenterx\ncentery\nclamp\nclamp_ip\nclip\ncollidedict\ncollidedictall\ncollidelist\ncollidelistall\ncollidepoint\ncolliderect\ncontains\nfit\nh\nheight\ninflate\ninflate_ip\nleft\nmidbottom\nmidleft\nmidright\nmidtop\nmove\nmove_ip\nnormalize\nright\nsize\ntop\ntopleft\ntopright\nunion\nunion_ip\nunionall\nunionall_ip\nw\nwidth\nx\ny", + "user_title" : "Anonymous", + "link" : "Surface.get_rect", + "datetimeon" : "2007-03-23T00:10:38", + "id" : 446 + }, + { + "id" : 1365, + "datetimeon" : "2008-01-04T23:39:18", + "link" : "pygame.mixer.music.fadeout", + "user_title" : "Anonymous", + "content" : "i've noticed the loop functionality to be iffy for certain wave files (an audible gap between each loop). from what i can tell, it looks like this happens with stereo wave files, but i'm not completely sure. the mono waves i try to loop play as expected" + }, + { + "datetimeon" : "2008-01-04T23:39:59", + "id" : 1366, + "user_title" : "Anonymous", + "content" : "i've noticed the loop functionality to be iffy for certain wave files (an audible gap between each loop). from what i can tell, it looks like this happens with stereo wave files, but i'm not completely sure. the mono waves i try to loop play as expected (i accidentally added this comment to fadeout(), sorry)", + "link" : "pygame.mixer.music.play" + }, + { + "datetimeon" : "2007-03-25T15:39:01", + "id" : 448, + "content" : "what about osx? is macosx working?", + "user_title" : "Anonymous", + "link" : "pygame.display.init" + }, + { + "user_title" : "Anonymous", + "content" : "omg, you should really use the KMOD_ constants here", + "link" : "pygame.key.get_mods", + "datetimeon" : "2007-03-28T15:20:15", + "id" : 451 + }, + { + "user_title" : "Anonymous", + "content" : "In 1.7.1, the behaviour when None is passed in is NOT reversed. pygame.event.set_allowed(None) will BLOCK all events.", + "link" : "pygame.event.set_allowed", + "datetimeon" : "2007-04-01T20:22:29", + "id" : 457 + }, + { + "id" : 463, + "datetimeon" : "2007-04-03T12:22:16", + "link" : "pygame.draw", + "user_title" : "Anonymous", + "content" : "just do:\nimg = pygame.image.load(\"<>\").convert()\n\n-harry666t" + }, + { + "id" : 466, + "datetimeon" : "2007-04-03T19:15:04", + "link" : "pygame.key.get_mods", + "user_title" : "Anonymous", + "content" : "BTW, Those values gotten below are if num-lock is on\nKMOD_NUM == 4096\nKMOD_LSHIFT == 1\nKMOD_RSHIFT == 2\nKMOD_NUM | KMOD_LSHIFT == 4097\nThe simpler way is to use the bitwise AND (&)\n\nkeymods & KMOD_LSHIFT \n\nreturns true (actually 1 in this case) if left shift is pressed, no matter what else is pressed or if num lock is on, or if the planets are aligned correctly." + }, + { + "id" : 475, + "datetimeon" : "2007-04-07T05:35:39", + "link" : "pygame.event", + "content" : "What is the definition of the key and mod members of KEYDOWN?", + "user_title" : "Anonymous" + }, + { + "link" : "pygame.mixer.set_reserved", + "content" : "who do i get a reference to a reserved channel? its not channel 0 nor num_channels-1", + "user_title" : "Anonymous", + "id" : 476, + "datetimeon" : "2007-04-07T11:19:42" + }, + { + "link" : "pygame.movie", + "content" : "\"does not work with current release\". Which release is that? Is the information valid?", + "user_title" : "Anonymous", + "id" : 497, + "datetimeon" : "2007-04-18T13:12:55" + }, + { + "link" : "pygame.mixer.Sound", + "content" : "FLAC support would be cool", + "user_title" : "Anonymous", + "id" : 1142, + "datetimeon" : "2007-11-19T06:00:08" + }, + { + "datetimeon" : "2008-01-01T21:41:36", + "id" : 1351, + "user_title" : "Anonymous", + "content" : "fadeout does not block in linux either", + "link" : "pygame.mixer.music.fadeout" + }, + { + "datetimeon" : "2007-04-20T17:57:54", + "id" : 502, + "content" : "Watch out for this one, it has a major twist:\n(x,y) are coordinates in the referential of the rectangle.\nFor instance:\n>>> import pygame\n>>> r = pygame.Rect(32,32,132,132)\n>>> r.collidepoint(140,140)\n1", + "user_title" : "Anonymous", + "link" : "Rect.collidepoint" + }, + { + "datetimeon" : "2008-01-13T07:45:26", + "id" : 1406, + "content" : "Music will be resampled in some cases, not in others. When playing a 44.1kHz MP3, the default 22050 frequency works, but a 48kHz mp3 plays in less than half speed - 48000 or 24000 works then.\nTo handle this behaviour, you have to know the sample rate of your music files before playing them, and can't switch smoothly. Big bummer.", + "user_title" : "Anonymous", + "link" : "pygame.mixer.music.play" + }, + { + "id" : 1220, + "datetimeon" : "2007-12-06T15:43:11", + "link" : "pygame.draw.rect", + "content" : "# This should draw a square with a hight of 20 pixels on a Surface:\nheight = 20\npygame.draw.rect(Surface, (255, 255, 255), (0, 0, height, height))", + "user_title" : "Anonymous" + }, + { + "content" : "Anonymous[0], that's nonsense. The x,y coords are absolute coordinates. To illustrate:\n\n>>> r = pygame.rect.Rect(32, 32, 132, 132)\n>>> r.collidepoint(1,1)\n0\n>>> r.collidepoint(32,32)\n1", + "user_title" : "Anonymous", + "link" : "Rect.collidepoint", + "datetimeon" : "2007-11-23T19:43:53", + "id" : 1153 + }, + { + "id" : 1157, + "datetimeon" : "2007-11-26T11:20:04", + "link" : "pygame.mouse.set_cursor", + "content" : "A little black cross. Mouse cursor is 8*8 Pixel, hotspot is at (4, 4). \nthe cross is (Read Binary):\n00011000 => 24 \n00011000\n00011000\n11100111 => 231\n11100111\n00011000\n00011000\nand has no AND-Mask. \n\npygame.mouse.set_cursor((8, 8), (4, 4), (24, 24, 24, 231, 231, 24, 24, 24), (0, 0, 0, 0, 0, 0, 0, 0))", + "user_title" : "Anonymous" + }, + { + "id" : 1158, + "datetimeon" : "2007-11-26T19:56:49", + "link" : "Surface.fill", + "user_title" : "Anonymous", + "content" : "excellent comments!\njorgen" + }, + { + "link" : "pygame.event.set_blocked", + "user_title" : "Anonymous", + "content" : "True. set_allowed(None) blocks all event types.\n\n- Another (initially skeptical) pygame user.", + "id" : 1159, + "datetimeon" : "2007-11-26T22:35:30" + }, + { + "id" : 1160, + "datetimeon" : "2007-11-27T12:11:21", + "link" : "pygame.mixer.set_reserved", + "user_title" : "Anonymous", + "content" : "The first channels are reserved.\nFor example: pygame.mixer.Channel(0)" + }, + { + "id" : 1230, + "datetimeon" : "2007-12-08T11:08:32", + "link" : "pygame.joystick", + "user_title" : "Anonymous", + "content" : "I have found that just watching for joystick events may not provide enough \ngranularity for fast-paced arcade games that require 100 millisecond changes.\nInstead of events, consider polling the status of the axes in the main game loop\n(or whatever your local equivalent is)" + }, + { + "datetimeon" : "2007-12-12T08:11:00", + "id" : 1242, + "user_title" : "Anonymous", + "content" : "Actually, on my system [Ubunty Gutsy] it returned a list of None:\n>>> import pygame\n>>> pygame.font.get_fonts()\n[None]", + "link" : "pygame.font.get_fonts" + }, + { + "id" : 1171, + "datetimeon" : "2007-11-29T13:19:20", + "link" : "pygame.draw.rect", + "content" : "How do you draw squares in pygame??", + "user_title" : "Anonymous" + }, + { + "content" : "\"... will only effect the smaller area\" is probably meant to read \"... will only affect the smaller area\"", + "user_title" : "Anonymous", + "link" : "pygame.Surface", + "datetimeon" : "2007-12-20T07:32:23", + "id" : 1290 + }, + { + "id" : 1320, + "datetimeon" : "2007-12-23T15:46:20", + "link" : "pygame.draw.circle", + "user_title" : "Anonymous", + "content" : "# Matthew N. Brown copyright 2007\n\n# Here is an example program in wich\n# balls hit walls and other balls:\n#\n# This program draws circles using: pygame.draw.circle\n#\n# You can copy this program on to\n# your own computer and run it.\n#\n\nimport os, sys\n\n ## INIT STUFF!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\ndef HE_HE_init():\n global screen, big_black_rect, APPLICATION_w_size, APPLICATION_z_size\n global WOW_pi_divided_by_180, WOW_180_divided_by_pi\n pygame.init()\n random.seed()\n APPLICATION_w_size = 700\n APPLICATION_z_size = 500\n ##### To close window while in fullscreen, press Esc while holding shift. #######\n screen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size))\n #screen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), FULLSCREEN)\n pygame.display.set_caption(\"They bwounce off bwalls? Matthew N. Brown copyright 2007\")\n pygame.mouse.set_visible(1)\n big_black_rect = pygame.Surface(screen.get_size())\n big_black_rect = big_black_rect.convert()\n big_black_rect.fill((0, 0, 0))\n screen.blit(big_black_rect, (0, 0))\n #fonty = pygame.font.Font(None, 36)\n fonty = pygame.font.SysFont(\"Times New Roman\", 25)\n fonty.set_bold(0)\n IMAGEE = fonty.render('Loading . . .', 1, (0, 250, 10))\n screen.blit(IMAGEE, (100, 200)); del IMAGEE\n pygame.display.flip()\n pygame.mixer.init(22050, -16, True, 1024)\n WOW_pi_divided_by_180 = math.pi / 180.0\n WOW_180_divided_by_pi = 180.0 / math.pi\n set_up_key_variables()\n Lets_ROLL()\n ## INIT STUFF!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\n\n ## SAVE LEVEL?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\ndef write_to_file_WEEE_STRANGE(file_namey, data):\n noq = '\\n'\n filey = open(file_namey, 'w')\n for d in data:\n filey.write( str(d) + noq)\n ## SAVE LEVEL?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\n\n ## SMALL FUNCTIONS STUFF!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\n ### some functions: ###\ndef distance_2D (w1, z1, w2, z2):\n return math.sqrt(math.pow(float(w1) - float(w2), 2) + math.pow(float(z1) - float(z2), 2))\ndef rect_touching_rect(w1, z1, wol1, zol1, w2, z2, wol2, zol2):\n w2 -= w1\n z2 -= z1\n ww1 = -wol2\n zz1 = -zol2\n return (w2 > ww1 and w2 < wol1 and z2 > zz1 and z2 < zol1)\ndef rect_touching_rect2(w1, z1, wol1, zol1, w2, z2, wol2, zol2):\n w2 -= w1\n z2 -= z1\n ww1 = -wol2\n zz1 = -zol2\n return (w2 >= ww1 and w2 <= wol1 and z2 >= zz1 and z2 <= zol1)\ndef positive(n):\n if n < 0: n = -n; return n\ndef int_randy(range, add):\n return int((random.random() * range) + add)\ndef randy(range, add):\n return (random.random() * range) + add\ndef freaky_rect_switcharoo_2D(pw, pz, pwol, pzol, buffy_the_fat):\n buffy_the_fat2 = buffy_the_fat * 2\n if pwol > 0:\n gw = pw; gwol = pwol\n else:\n gw = pwol + pw; gwol = pw - gw\n if pzol > 0:\n gz = pz; gzol = pzol\n else:\n gz = pzol + pz; gzol = pz - gz\n return [gw - buffy_the_fat, gz - buffy_the_fat, gwol + buffy_the_fat2, gzol + buffy_the_fat2]\ndef points_rotated_by_angle_2D(points_wz, axis_w, axis_z, angle):\n rotated_points_wz = []\n angle = -angle -90\n angle_times_WOW_pi_divided_by_180 = angle * WOW_pi_divided_by_180\n c1 = math.cos(angle_times_WOW_pi_divided_by_180)\n s1 = math.sin(angle_times_WOW_pi_divided_by_180)\n for pointy in points_wz:\n xt = pointy[0] - axis_w\n yt = pointy[1] - axis_z\n rotated_points_wz += [(-xt * s1) + (yt * c1) + axis_w, (-xt * c1) - (yt * s1) + axis_z]\n return rotated_points_wz\ndef point_rotated_by_angle_2D(point_w, point_z, axis_w, axis_z, angle):\n angle = -angle -90\n angle_times_WOW_pi_divided_by_180 = angle * WOW_pi_divided_by_180\n c1 = math.cos(angle_times_WOW_pi_divided_by_180)\n s1 = math.sin(angle_times_WOW_pi_divided_by_180)\n xt = point_w - axis_w\n yt = point_z - axis_z\n return (-xt * s1) + (yt * c1) + axis_w, (-xt * c1) - (yt * s1) + axis_z\ndef arc_tangent_2D(point_w, point_z):\n return math.atan2(point_w, point_z) * WOW_180_divided_by_pi + 180\ndef arc_tangent_2D_2(point_w, point_z):\n return -math.atan2(point_w, point_z) * WOW_180_divided_by_pi + 180\ndef ball_to_ball_wzkol_bounce(V1, m1, V2, m2, ball1_is_to_the_left):\n if (ball1_is_to_the_left and V1 >= V2) or (not ball1_is_to_the_left and V1 <= V2):\n Rv1 = V1 - V2\n Rv2 = 0 #V2 - V2\n NewV1 = ((m1 - m2) / float(m1 + m2)) * float(Rv1) + V2\n NewV2 = (( 2 * m1) / float(m1 + m2)) * float(Rv1) + V2\n return NewV1, NewV2\n else:\n return V1, V2\ndef Find_where_ball_stops_on_line_w(ball_w, ball_z, ball_wol, ball_zol, ball_rad, line_w, line_rad):\n did_collide = False\n totally = ball_rad + line_rad\n b1 = line_w + totally\n b2 = line_w - totally\n New_ball_w = ball_w + ball_wol\n New_ball_z = ball_z + ball_zol\n if ball_w >= b1 and ball_wol < 0 and New_ball_w < b1: New_ball_w = b1; did_collide = True\n elif ball_w <= b2 and ball_wol > 0 and New_ball_w > b2: New_ball_w = b2; did_collide = True\n else:\n if ball_w > b2 and ball_w < b1:\n if ball_w > line_w and ball_wol < 0:\n New_ball_w = ball_w; New_ball_z = ball_z\n did_collide = True\n elif ball_w < line_w and ball_wol > 0:\n New_ball_w = ball_w; New_ball_z = ball_z\n did_collide = True\n return New_ball_w, New_ball_z, did_collide\n New_ball_z = (float(ball_zol) / float(ball_wol) * float(New_ball_w - ball_w)) + float(ball_z)\n return New_ball_w, New_ball_z, did_collide\ndef find_where_ball_collides_on_a_wall(\n ball_w, ball_z,\n ball_wol, ball_zol,\n ball_rad,\n wall_type,\n wall_w1, wall_z1,\n wall_w2, wall_z2,\n wall_rad):\n toetoadly = ball_rad + wall_rad\n did_collide = False\n New_ball_w = ball_w + ball_wol\n New_ball_z = ball_z + ball_zol\n angle_hit_at = None\n Relate_ball_w = ball_w - wall_w1\n Relate_ball_z = ball_z - wall_z1\n Relate_wall_w2 = wall_w2 - wall_w1\n Relate_wall_z2 = wall_z2 - wall_z1\n arc_tangeriney = arc_tangent_2D(Relate_wall_w2, Relate_wall_z2)\n Rotate_Relate_ball_w, Rotate_Relate_ball_z, Rotate_Relate_wall_w2, Rotate_Relate_wall_z2 = points_rotated_by_angle_2D(((Relate_ball_w, Relate_ball_z), (Relate_wall_w2, Relate_wall_z2)), 0, 0, arc_tangeriney)\n Rotate_ball_wol, Rotate_ball_zol = point_rotated_by_angle_2D(ball_wol, ball_zol, 0, 0, arc_tangeriney)\n Rotate_Relate_ball_collide_w, Rotate_Relate_ball_collide_z, did_hit_weird_line = Find_where_ball_stops_on_line_w(Rotate_Relate_ball_w, Rotate_Relate_ball_z, Rotate_ball_wol, Rotate_ball_zol, ball_rad, 0, wall_rad)\n if Rotate_Relate_ball_w > -toetoadly and Rotate_Relate_ball_w < toetoadly:\n HE_HE_strange_popper_z = Rotate_Relate_ball_z\n else:\n HE_HE_strange_popper_z = Rotate_Relate_ball_collide_z\n Rotate_angle_hit_at = None\n if HE_HE_strange_popper_z < Rotate_Relate_wall_z2:\n if ball_is_going_towards_point(Rotate_Relate_ball_w, Rotate_Relate_ball_z, Rotate_ball_wol, Rotate_ball_zol, 0, Rotate_Relate_wall_z2):\n p1_touched, p1_collide_w, p1_collide_z, p1_angle_hit_at = find_where_ball_collides_on_another_ball(Rotate_Relate_ball_w, Rotate_Relate_ball_z, Rotate_ball_wol, Rotate_ball_zol, ball_rad, 0, Rotate_Relate_wall_z2, wall_rad)\n if p1_touched:\n Rotate_Relate_ball_collide_w = p1_collide_w\n Rotate_Relate_ball_collide_z = p1_collide_z\n Rotate_angle_hit_at = p1_angle_hit_at\n did_collide = True\n elif HE_HE_strange_popper_z > 0:\n if ball_is_going_towards_point(Rotate_Relate_ball_w, Rotate_Relate_ball_z, Rotate_ball_wol, Rotate_ball_zol, 0, 0):\n p2_touched, p2_collide_w, p2_collide_z, p2_angle_hit_at = find_where_ball_collides_on_another_ball(Rotate_Relate_ball_w, Rotate_Relate_ball_z, Rotate_ball_wol, Rotate_ball_zol, ball_rad, 0, 0, wall_rad)\n if p2_touched:\n Rotate_Relate_ball_collide_w = p2_collide_w\n Rotate_Relate_ball_collide_z = p2_collide_z\n Rotate_angle_hit_at = p2_angle_hit_at\n did_collide = True\n else:\n if did_hit_weird_line:\n did_collide = True\n if Rotate_Relate_ball_collide_w < 0: Rotate_angle_hit_at = 90\n else: Rotate_angle_hit_at = 270\n if did_collide:\n arc_tangeriney_2 = -arc_tangeriney\n angle_hit_at = Rotate_angle_hit_at + arc_tangeriney\n New_ball_w, New_ball_z = point_rotated_by_angle_2D(Rotate_Relate_ball_collide_w, Rotate_Relate_ball_collide_z, 0, 0, arc_tangeriney_2)\n New_ball_w += wall_w1\n New_ball_z += wall_z1\n return did_collide, New_ball_w, New_ball_z, angle_hit_at #, is_moving_towards\ndef zol_at_angle(wol, zol, angle):\n rotated_wol, rotated_zol = point_rotated_by_angle_2D(wol, zol, 0, 0, angle)\n return rotated_zol\ndef wzol_bounce_at_angle(wol, zol, angle, multi):\n rotated_wol, rotated_zol = point_rotated_by_angle_2D(wol, zol, 0, 0, angle)\n if rotated_zol > 0: rotated_zol = -rotated_zol * multi\n return point_rotated_by_angle_2D(rotated_wol, rotated_zol, 0, 0, -angle)\ndef ball_is_going_towards_point(ball_w, ball_z, ball_wol, ball_zol, point_w, point_z):\n angley = arc_tangent_2D(ball_w - point_w, ball_z - point_z)\n rotated_wol, rotated_zol = point_rotated_by_angle_2D(ball_wol, ball_zol, 0, 0, angley)\n return rotated_zol > 0\ndef find_where_ball_collides_on_another_ball (\n ball1_w, ball1_z,\n ball1_wol, ball1_zol,\n ball1_rad,\n ball2_w, ball2_z,\n ball2_rad\n ):\n totally = ball1_rad + ball2_rad\n dis_from_each_other = math.sqrt(math.pow(float(ball1_w) - float(ball2_w), 2) + math.pow(float(ball1_z) - float(ball2_z), 2))\n if dis_from_each_other < totally:\n angley = arc_tangent_2D(ball1_w - ball2_w, ball1_z - ball2_z)\n return True, ball1_w, ball1_z, angley\n else:\n they_did_touch = False\n New_ball1_w = ball1_w + ball1_wol\n New_ball1_z = ball1_z + ball1_zol\n angle_hit_at = None\n Relate_ball1_w = ball1_w - ball2_w\n Relate_ball1_z = ball1_z - ball2_z\n Relate_ball2_w = 0\n Relate_ball2_z = 0\n arcy_tangeriney = arc_tangent_2D(ball1_wol, ball1_zol)\n Rotated_Relate_ball1_w, Rotated_Relate_ball1_z, Rotated_ball1_wol, Rotated_ball1_zol = points_rotated_by_angle_2D(((Relate_ball1_w, Relate_ball1_z), (ball1_wol, ball1_zol)), 0, 0, arcy_tangeriney)\n did_collidey = False\n if Rotated_Relate_ball1_z > 0 and (Rotated_Relate_ball1_w > -totally and Rotated_Relate_ball1_w < totally):\n Rotated_Relate_ball1_collide_w = Rotated_Relate_ball1_w # + Rotated_ball1_wol\n HE_HE = math.pow(Rotated_Relate_ball1_w, 2) - math.pow(totally, 2)\n if HE_HE < 0: HE_HE = -HE_HE\n Rotated_Relate_ball1_collide_z = math.sqrt(HE_HE)\n Rotated_Relate_ball1_z__PLUS__Rotated_ball1_zol = Rotated_Relate_ball1_z + Rotated_ball1_zol\n if Rotated_Relate_ball1_collide_z < Rotated_Relate_ball1_z__PLUS__Rotated_ball1_zol:\n collision_wol = Rotated_ball1_wol\n collision_zol = Rotated_ball1_zol\n Rotated_Relate_ball1_collide_z = Rotated_Relate_ball1_z__PLUS__Rotated_ball1_zol\n angley_to_hit = None\n else:\n did_collidey = True\n they_did_touch = True\n angley_to_hit = arc_tangent_2D(Rotated_Relate_ball1_collide_w, Rotated_Relate_ball1_collide_z)\n else:\n angley_to_hit = None\n collision_wol = Rotated_ball1_wol\n collision_zol = Rotated_ball1_zol\n Rotated_Relate_ball1_collide_w = Rotated_Relate_ball1_w + Rotated_ball1_wol\n Rotated_Relate_ball1_collide_z = Rotated_Relate_ball1_z + Rotated_ball1_zol\n if did_collidey:\n arcy_tangeriney_2 = -arcy_tangeriney\n angle_hit_at = angley_to_hit + arcy_tangeriney\n New_ball1_w, New_ball1_z = point_rotated_by_angle_2D(Rotated_Relate_ball1_collide_w, Rotated_Relate_ball1_collide_z, 0, 0, arcy_tangeriney_2)\n New_ball1_w += ball2_w\n New_ball1_z += ball2_z\n return they_did_touch, New_ball1_w, New_ball1_z, angle_hit_at #, New_ball1_wol, New_ball1_zol\n ### some functions: ###\n\n ## GRAPHICS STUFF!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\ndef chilly_font(size):\n fonti = pygame.font.SysFont(\"Times New Roman\", size)\n return fonti\ndef chilly_font_Italicy(size):\n fonti = pygame.font.SysFont(\"Times New Roman\", size)\n fonti.set_italic(1)\n return fonti\ndef draw_loading_messagey(stringy): # Draw loading message\n pygame.mouse.set_visible(1)\n fonty = chilly_font(26)\n IMAGEE = fonty.render(stringy, 0, (0, 255, 0), (0, 0, 0))\n screen.blit(IMAGEE, (200, 250))\n del IMAGEE\n pygame.display.flip()\n ## GRAPHICS STUFF: ##\n#########################################################################################\n\n ## KEYS AND MOUSE STUFF!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\ndef set_up_key_variables():\n global ky_held, ky_first_held, ky_time_last_pressed\n global mowse_w, mowse_z, mowse_inn\n global mowse_left_pressed, mowse_right_pressed, mowse_left_held, mowse_right_held\n mowse_left_held = False\n mowse_right_held = False\n mowse_left_pressed = False\n mowse_right_pressed = False\n mowse_w = 0\n mowse_z = 0\n mowse_inn = 0\n ky_held = []\n ky_first_held = []\n ky_time_last_pressed = []\n m = -1\n while m < 500:\n m += 1\n ky_held += [0]\n ky_first_held += [0]\n ky_time_last_pressed += [0]\ndef clear_all_kys():\n global mowse_left_pressed, mowse_right_pressed, mowse_left_held, mowse_right_held\n mowse_left_held = False\n mowse_right_held = False\n mowse_left_pressed = False\n mowse_right_pressed = False\n m = -1\n while (m < 500):\n m += 1; ky_held[m] = 0; ky_first_held[m] = 0; ky_time_last_pressed[m] = 0\ndef clear_these_ky_first_held(list_keys_numbers):\n for k in list_keys_numbers:\n ky_first_held[k] = 0\ndef clear_first_held_kys():\n m = -1\n while (m < 500):\n m += 1; ky_first_held[m] = 0\ndef old_style_ky(n):\n return (ky_first_held_CEV(n) or (ky_held[n] and ky_time_last_pressed[n] < time.time() - .3))\ndef ky_first_held_CEV(n):\n if (ky_first_held[n]):\n ky_first_held[n] = 0; return 1\n else:\n return 0\ndef mowse_in_rect (w, z, wol, zol):\n return (mowse_w >= w and mowse_z >= z and mowse_w <= w + wol and mowse_z <= z + zol)\ndef mowse_in_circle (w, z, rad):\n dia = rad * 2\n if mowse_in_rect(w - rad, z - rad, w + dia, z + dia):\n return (distance_2D(mowse_w, mowse_z, w, z) < rad)\n else:\n return 0\n ## CHECK FOR: KEYBOARD, MOUSE, JOYSTICK, AND OTHERY INPUTY: ##\ndef check_for_keys():\n global mowse_w, mowse_z, mowse_inn, mowse_left_pressed, mowse_right_pressed, mowse_left_held, mowse_right_held, APPLICATION_w_size, APPLICATION_z_size\n global loopy\n global unicodey\n mowse_left_pressed = False\n mowse_right_pressed = False\n unicodey = ''\n for e in pygame.event.get():\n if e.type == QUIT:\n loopy = 0\n elif e.type == ACTIVEEVENT:\n mowse_inn = (e.gain and (e.state == 1 or e.state == 6))\n elif e.type == KEYDOWN:\n ky_held[e.key] = 1\n ky_first_held[e.key] = 1\n ky_time_last_pressed[e.key] = time.time()\n unicodey = e.unicode\n elif e.type == KEYUP:\n ky_held[e.key] = 0\n elif e.type == MOUSEMOTION:\n mowse_w = e.pos[0]\n mowse_z = e.pos[1]\n if mowse_w >= 0 and mowse_w <= APPLICATION_w_size and mowse_z >= 0 and mowse_z <= APPLICATION_z_size:\n mowse_inn = 1\n else:\n mowse_inn = 0\n elif e.type == MOUSEBUTTONUP:\n if e.button == 1: mowse_left_held = 0\n if e.button == 3: mowse_right_held = 0\n elif e.type == MOUSEBUTTONDOWN:\n mowse_left_pressed = (e.button == 1)\n mowse_right_pressed = (e.button == 3)\n mowse_left_held = mowse_left_held or e.button == 1\n mowse_right_held = mowse_right_held or e.button == 3\n elif e.type == JOYAXISMOTION:\n pass\n elif e.type == JOYBALLMOTION:\n pass\n elif e.type == JOYHATMOTION:\n pass\n elif e.type == JOYBUTTONUP:\n pass\n elif e.type == JOYBUTTONDOWN:\n pass\n elif e.type == VIDEORESIZE:\n print e\n print \"What happened!?\"\n #global big_black_rect, screen\n #APPLICATION_w_size = e.size[0]\n #APPLICATION_z_size = e.size[1]\n #screen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size))#, RESIZABLE)\n #big_black_rect = pygame.Surface(screen.get_size())\n #big_black_rect = big_black_rect.convert()\n #big_black_rect.fill((0, 100, 200))\n elif e.type == VIDEOEXPOSE:\n pass\n elif e.type == USEREVENT:\n pass\n if ky_held[27] and (ky_held[303] or ky_held[304]): loopy = 0\n ## CHECK FOR: KEYBOARD, MOUSE, JOYSTICK, AND OTHERY INPUTY: ##\n ## KEYS AND MOUSE STUFF!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\n\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n\n\n ## MAIN LOOPY STUFF!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\ndef ball_is_going_towards_ball(Bn1, Bn2):\n global ball_max, ball_w, ball_z, ball_wol, ball_zol, ball_rad, ball_color, ball_mass, ball_RECT\n arc_tangerine = arc_tangent_2D(ball_w[Bn1] - ball_w[Bn2], ball_z[Bn1] - ball_z[Bn2])\n woly1, zoly1 = point_rotated_by_angle_2D(ball_wol[Bn1], ball_zol[Bn1], 0, 0, arc_tangerine)\n return zoly1 > 0\ndef ball_is_relatively_going_towards_ball(Bn1, Bn2):\n global ball_max, ball_w, ball_z, ball_wol, ball_zol, ball_rad, ball_color, ball_mass, ball_RECT\n arc_tangerine = arc_tangent_2D(ball_w[Bn1] - ball_w[Bn2], ball_z[Bn1] - ball_z[Bn2])\n woly1, zoly1, woly2, zoly2 = points_rotated_by_angle_2D(((ball_wol[Bn1], ball_zol[Bn1]), (ball_wol[Bn2], ball_zol[Bn2])), 0, 0, arc_tangerine)\n return zoly1 > 0 and zoly1 > zoly2 # zoly2 < zoly1 or zoly2 > zoly1 # zoly1 + zoly2 > 0\n #return zoly1 > 0 or zoly1 > zoly2\ndef Make_two_balls_hit_at_angle(Bn1, Bn2, angle):\n global bounce_friction\n #print angle\n global ball_max, ball_w, ball_z, ball_wol, ball_zol, ball_rad, ball_color, ball_mass, ball_RECT\n woly1, zoly1, woly2, zoly2 = points_rotated_by_angle_2D(((ball_wol[Bn1], ball_zol[Bn1]), (ball_wol[Bn2], ball_zol[Bn2])), 0, 0, angle)\n V1 = zoly1 * bounce_friction\n V2 = zoly2 * bounce_friction\n zoly1, zoly2 = ball_to_ball_wzkol_bounce(V1, ball_mass[Bn1], V2, ball_mass[Bn2], True)\n ball_wol[Bn1], ball_zol[Bn1], ball_wol[Bn2], ball_zol[Bn2] = points_rotated_by_angle_2D(((woly1, zoly1), (woly2, zoly2)), 0, 0, -angle)\n updatey_ball_quick_rect(Bn1)\n updatey_ball_quick_rect(Bn2)\ndef updatey_ball_quick_rect(B):\n dia = ball_rad[B] * 2 + 4\n ball_squar[B] = [ball_w[B] - ball_rad[B] - 2, ball_z[B] - ball_rad[B] - 2, dia, dia]\n ball_RECT[B] = freaky_rect_switcharoo_2D(ball_w[B], ball_z[B], ball_wol[B], ball_zol[B], ball_rad[B] + 4)\ndef minus_ball_thing(n):\n global ball_max, ball_w, ball_z, ball_wol, ball_zol, ball_rad, ball_color, ball_angle, ball_angleol, ball_squar, ball_mass, ball_RECT\n if ball_max >= 0:\n del ball_w [n]\n del ball_z [n]\n del ball_wol [n]\n del ball_zol [n]\n del ball_rad [n]\n del ball_color [n]\n del ball_squar [n]\n del ball_angle [n]\n del ball_angleol[n]\n del ball_mass [n]\n del ball_RECT [n]\n ball_max -= 1\ndef add_ball_thing(w, z, wol, zol, rad, color, angle, angleol, mass_thing, rect_thing):\n global ball_max, ball_w, ball_z, ball_wol, ball_zol, ball_rad, ball_color, ball_squar, ball_angle, ball_angleol, ball_mass, ball_RECT\n ball_max += 1\n ball_w += [w]\n ball_z += [z]\n ball_wol += [wol]\n ball_zol += [zol]\n ball_rad += [rad]\n ball_color += [color]\n ball_angle += [angle]\n ball_angleol += [angleol]\n dia = rad * 2\n ball_squar += [[w - rad, z - rad, dia, dia]]\n if mass_thing == True:\n ball_mass += [4 / 3 * math.pi * rad * rad * rad]\n else:\n ball_mass += [mass_thing]\n if rect_thing == True:\n ball_RECT += [None]\n updatey_ball_quick_rect(ball_max)\n #ball_RECT += [freaky_rect_switcharoo_2D(w, z, wol, zol, rad)]\n else:\n ball_RECT += [rect_thing]\ndef minus_wall_thing(WAL):\n global wall_max, wall_type, wall_w1, wall_z1, wall_w2, wall_z2, wall_rad, wall_color, wall_RECT\n if wall_max >= 0:\n del wall_type [WAL]\n del wall_w1 [WAL]\n del wall_z1 [WAL]\n del wall_w2 [WAL]\n del wall_z2 [WAL]\n del wall_rad [WAL]\n del wall_color [WAL]\n del wall_RECT [WAL]\n wall_max -= 1\ndef add_wall_thing(type, w1, z1, w2, z2, rad, color_thing, rect_thing):\n global wall_max, wall_type, wall_w1, wall_z1, wall_w2, wall_z2, wall_rad, wall_color, wall_RECT\n wall_max += 1\n wall_type += [type]\n wall_w1 += [w1]\n wall_z1 += [z1]\n wall_w2 += [w2]\n wall_z2 += [z2]\n wall_rad += [rad]\n if color_thing == True:\n if type == 1: color_thing = (220, 220, 220)\n elif type == 2: color_thing = (240, 140, 130)\n elif type == 3: color_thing = (100, 255, 100)\n elif type == 4: color_thing = (255, 100, 100)\n elif type == 5: color_thing = (100, 100, 255)\n wall_color += [color_thing]\n if rect_thing == True:\n wall_RECT += [freaky_rect_switcharoo_2D(w1 - 2, z1 - 2, w2 - w1 + 4, z2 - z1 + 4, rad)]\n else:\n wall_RECT += [rect_thing]\ndef reset_stuff():\n global ball_max, ball_w, ball_z, ball_wol, ball_zol, ball_rad, ball_color, ball_angle, ball_angleol, ball_squar, ball_mass, ball_RECT\n global wall_max, wall_type, wall_w1, wall_z1, wall_w2, wall_z2, wall_rad, wall_color, wall_RECT\n global levely\n if levely == 1:\n ball_max = -1\n ball_w = []\n ball_z = []\n ball_wol = []\n ball_zol = []\n ball_rad = []\n ball_color = []\n ball_angle = []\n ball_angleol = []\n ball_squar = []\n ball_mass = []\n ball_RECT = []\n #add_ball_thing(350, 300, 0, 0, 18, (230, 230, 250), 0, 0, True, True)\n #add_ball_thing(150, 400, 0, 0, 40, (220, 210, 255), 0, 0, True, True)\n #add_ball_thing(300, 150, 0, 0, 62, (110, 106, 255), 0, 0, True, True)\n add_ball_thing(220, 200, 0, 0, 50, (180, 226, 255), 180, 0, True, True)\n wall_max = -1\n wall_type = []\n wall_w1 = []\n wall_z1 = []\n wall_w2 = []\n wall_z2 = []\n wall_rad = []\n wall_color = []\n wall_RECT = []\n add_wall_thing(1, 160, 250, 300, 270, 1, True, True)\n add_wall_thing(1, 500, 270, 600, 310, 1, True, True)\n add_wall_thing(1, 200, 450, 600, 450, 10, True, True)\n add_wall_thing(1, 300, 350, 400, 370, 5, True, True)\n add_wall_thing(1, 300, 100, 400, 100, 20, True, True)\n add_wall_thing(1, 650, 140, 700, 200, 6, True, True)\n add_wall_thing(1, 650, 140, 600, 40, 6, True, True)\n add_wall_thing(1, 150, 340, 150, 340, 30, True, True)\n add_wall_thing(1, 40, 200, 40, 200, 30, True, True)\n add_wall_thing(1, 30, 30, 30, 30, 10, True, True)\n add_wall_thing(1, 30, 30, 30, 30, 10, True, True)\n add_wall_thing(1, 30, 30, 30, 30, 10, True, True)\n add_wall_thing(1, 30, 30, 30, 30, 10, True, True)\n add_wall_thing(1, 30, 30, 30, 30, 10, True, True)\n add_wall_thing(1, 0, 0, APPLICATION_w_size, 0, 5, True, True)\n add_wall_thing(1, 0, 0, 0, APPLICATION_z_size, 5, True, True)\n add_wall_thing(1, 0, APPLICATION_z_size, APPLICATION_w_size, APPLICATION_z_size, 5, True, True)\n add_wall_thing(1, APPLICATION_w_size, 0, APPLICATION_w_size, APPLICATION_z_size, 5, True, True)\n elif levely == 2:\n ball_max = 1\n ball_w = [323.62638473709342, 384.72135876760257]\n ball_z = [298.67896746658624, 109.24043981044279]\n ball_wol = [-0.27396932987421913, 7.133321987715842]\n ball_zol = [-0.38420912894762504, 1.6564147490246901]\n ball_rad = [15, 28]\n ball_color = [(137, 244, 234), (138, 221, 217)]\n ball_angle = [51.908780125668613, 294.77431504891717]\n ball_angleol = [-1.2400074168431123, 17.698615258690229]\n ball_squar = [[306.62638473709342, 281.67896746658624, 34, 34], [354.72135876760257, 79.240439810442794, 60, 60]]\n ball_mass = [10602.875205865552, 68964.24193160313]\n ball_RECT = [[304.35241540721921, 279.2947583376386, 38.273969329874205, 38.384209128947646], [352.72135876760257, 77.240439810442794, 71.133321987715846, 65.656414749024691]]\n wall_max = 17\n wall_type = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n wall_w1 = [189, 290, 166, 14, 697, 562, 643, 3, 0, 223, 117, 695, 497, 497, 0, 0, 0, 700]\n wall_z1 = [284, 316, 436, 499, 446, 0, 128, 225, 106, 310, 155, 210, 159, 159, 0, 0, 500, 0]\n wall_w2 = [222, 446, 697, 157, 377, 681, 679, 49, 383, 287, 5, 448, 376, 546, 700, 0, 700, 700]\n wall_z2 = [301, 314, 478, 432, 487, 99, 98, 416, 171, 324, 225, 323, 147, 179, 0, 500, 500, 500]\n wall_rad = [1, 1, 10, 5, 20, 6, 6, 30, 30, 10, 10, 10, 10, 10, 5, 5, 5, 5]\n wall_color = [(220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220)]\n wall_RECT = [[186, 281, 39, 23], [287, 313, 162, 4], [154, 424, 555, 66], [7, 429, 157, 73], [359, 424, 356, 85], [554, -8, 135, 115], [635, 94, 52, 38], [-29, 193, 110, 255], [-32, 74, 447, 129], [211, 298, 88, 38], [-3, 143, 128, 94], [440, 198, 263, 137], [368, 139, 137, 28], [485, 147, 73, 44], [-7, -7, 714, 14], [-7, -7, 14, 514], [-7, 493, 714, 14], [693, -7, 14, 514]]\n elif levely == 3:\n ball_max = 2\n ball_w = [425.0, 492.31837629165733, 98.512856261065167]\n ball_z = [126.0, 422.24553778829392, 430.4902396760661]\n ball_wol = [-12.0, 2.6816237083426699, 6.487143738934833]\n ball_zol = [-3.0, -1.245537788293916, -21.490239676066096]\n ball_rad = [15, 28, 21]\n ball_color = [(137, 244, 234), (138, 221, 217), (136, 235, 236)]\n ball_angle = [93.833857527468922, 75.681742520058592, 323.2915629772819]\n ball_angleol = [-0.87655530207419896, 0.30220691772972269, 1.1825329351046094]\n ball_squar = [[408.0, 109.0, 34, 34], [462.31837629165733, 392.24553778829392, 60, 60], [75.512856261065167, 407.4902396760661, 46, 46]]\n ball_mass = [10602.875205865552, 68964.24193160313, 29094.28956489508]\n ball_RECT = [[394.0, 104.0, 50.0, 41.0], [460.31837629165733, 389.0, 66.68162370834267, 65.245537788293916], [73.512856261065167, 384.0, 56.487143738934833, 71.490239676066096]]\n wall_max = 17\n wall_type = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n wall_w1 = [189, 290, 166, 14, 697, 562, 643, 3, 0, 223, 117, 695, 497, 497, 0, 0, 0, 700]\n wall_z1 = [284, 316, 436, 499, 446, 0, 128, 225, 106, 310, 155, 210, 159, 159, 0, 0, 500, 0]\n wall_w2 = [222, 446, 697, 157, 377, 681, 679, 49, 383, 287, 5, 480, 376, 546, 700, 0, 700, 700]\n wall_z2 = [301, 314, 478, 432, 487, 99, 98, 416, 171, 324, 225, 325, 147, 179, 0, 500, 500, 500]\n wall_rad = [1, 1, 10, 5, 20, 6, 6, 30, 30, 10, 10, 10, 10, 10, 5, 5, 5, 5]\n wall_color = [(220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220)]\n wall_RECT = [[186, 281, 39, 23], [287, 313, 162, 4], [154, 424, 555, 66], [7, 429, 157, 73], [359, 424, 356, 85], [554, -8, 135, 115], [635, 94, 52, 38], [-29, 193, 110, 255], [-32, 74, 447, 129], [211, 298, 88, 38], [-3, 143, 128, 94], [472, 198, 231, 139], [368, 139, 137, 28], [485, 147, 73, 44], [-7, -7, 714, 14], [-7, -7, 14, 514], [-7, 493, 714, 14], [693, -7, 14, 514]]\n elif levely == 4:\n ball_max = 15\n ball_w = [60.722554805471077, 452.1573538490178, 80.244575784959252, 38.90004863123329, 526.62934623960155, 561.76077439217966, 51.00641675327735, 476.21179724447387, 74.019911348330012, 104.13986580489509, 77.672785567417591, 97.908669417930454, 492.31309851379422, 107.55531577343871, 25.677250467589708, 408.28461679522843]\n ball_z = [123.53309256655999, 426.85562864865636, 446.98025958602022, 145.55077237791539, 432.36880616921724, 419.52605372165829, 185.76812996010321, 398.60172712183214, 227.90675893521163, 330.14246403509031, 280.7917430301959, 382.77488932204739, 431.7008452670733, 426.72875393133694, 108.86075181750218, 420.07030113046562]\n ball_wol = [0.58974898201312453, 0.29357826379544644, -0.7453458908661944, -0.26977452024547638, -0.13077525550683244, 0.35703289164546842, 0.25581836770201244, -0.16968524576896582, -0.96858759109981474, 0.020541831638986374, 0.21623640500730243, 0.16869582232640204, -0.32778500262837312, -1.0423733543425631, 0.078384075232750969, 0.070169924397188832]\n ball_zol = [2.5202528491916918, -0.067935899483811957, 1.0209651395893582, 1.5519551597452736, 0.37674466231734333, 0.7179102343171756, 1.2098558443319702, -0.21937811619009639, 1.6292902773669935, 0.95366629391114355, 0.99836183708718151, 0.65985328138026611, 0.72997687518744558, -0.33325230167901332, 1.8584237502130836, 1.1180771215980612]\n ball_rad = [12, 20, 14, 19, 14, 23, 23, 13, 25, 28, 28, 25, 20, 20, 20, 24]\n ball_color = [(132, 202, 208), (130, 220, 228), (133, 230, 241), (133, 200, 224), (138, 244, 248), (134, 176, 212), (132, 246, 206), (136, 191, 201), (130, 247, 204), (135, 190, 248), (136, 196, 244), (137, 246, 211), (132, 176, 232), (139, 200, 204), (135, 204, 206), (137, 234, 248)]\n ball_angle = [250.64218161257492, 228.50285566079282, 169.93029421257162, 93.92451866434908, 160.53385135173758, 101.81391124171368, 58.682544988047297, 42.833392250734839, 278.96920717602609, 157.52451729820555, 104.82808146227505, 319.29094377305643, 8.3988066326588289, 61.303383965779759, 262.01723832271352, 187.75853100116501]\n ball_angleol = [-11.145052526574146, 0.73910476098485844, -1.916370769365741, 7.8109934129380036, 1.2564621818214414, -0.21633250902344123, 0.96094866236460608, 18.696614939999161, -2.7765510174821686, -0.46915418861267033, 1.3615127061730832, 0.55215997018655683, 0.83188571652892485, -2.1096665563746759, 4.3536534603644128, 0.77565328887569629]\n ball_squar = [[46.722554805471077, 109.53309256655999, 28, 28], [430.1573538490178, 404.85562864865636, 44, 44], [64.244575784959252, 430.98025958602022, 32, 32], [17.90004863123329, 124.55077237791539, 42, 42], [510.62934623960155, 416.36880616921724, 32, 32], [536.76077439217966, 394.52605372165829, 50, 50], [26.00641675327735, 160.76812996010321, 50, 50], [461.21179724447387, 383.60172712183214, 30, 30], [47.019911348330012, 200.90675893521163, 54, 54], [74.139865804895095, 300.14246403509031, 60, 60], [47.672785567417591, 250.7917430301959, 60, 60], [70.908669417930454, 355.77488932204739, 54, 54], [470.31309851379422, 409.7008452670733, 44, 44], [85.555315773438707, 404.72875393133694, 44, 44], [3.6772504675897082, 86.860751817502177, 44, 44], [382.28461679522843, 394.07030113046562, 52, 52]]\n ball_mass = [5428.6721054031623, 25132.741228718347, 8620.5302414503913, 21548.184010972389, 8620.5302414503913, 38223.757816227015, 38223.757816227015, 6902.0790599367756, 49087.385212340516, 68964.24193160313, 68964.24193160313, 49087.385212340516, 25132.741228718347, 25132.741228718347, 25132.741228718347, 43429.376843225298]\n tempy = [[24.00641675327735, 158.76812996010321, 54.255818367702012, 55.209855844331969], [459.04211199870491, 381.38234900564203, 34.16968524576896, 34.219378116190114], [44.051323757230193, 198.90675893521163, 58.968587591099819, 59.629290277366991], [72.139865804895095, 298.14246403509031, 64.02054183163898, 64.953666293911141], [45.672785567417591, 248.7917430301959, 64.216236405007308, 64.998361837087188], [68.908669417930454, 353.77488932204739, 58.168695822326399, 58.659853281380265], [467.98531351116583, 407.7008452670733, 48.327785002628389, 48.729976875187447], [82.512942419096149, 402.39550162965793, 49.042373354342558, 48.333252301679011], [1.6772504675897082, 84.860751817502177, 48.078384075232748, 49.858423750213085], [380.28461679522843, 392.07030113046562, 56.070169924397192, 57.118077121598063]]\n ball_RECT = [[44.722554805471077, 107.53309256655999, 32.589748982013127, 34.520252849191692], [428.1573538490178, 402.78769274917255, 48.293578263795446, 48.067935899483814], [61.499229894093062, 428.98025958602022, 36.74534589086619, 37.020965139589357], [15.630274110987813, 122.55077237791539, 46.269774520245477, 47.551955159745276], [508.49857098409473, 414.36880616921724, 36.130775255506819, 36.376744662317343], [534.76077439217966, 392.52605372165829, 54.357032891645467, 54.717910234317173]] + tempy\n del tempy\n wall_max = 17\n wall_type = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n wall_w1 = [189, 196, 166, 14, 697, 562, 643, 0, 326, 51, 18, 695, 497, 497, 0, 0, 0, 700]\n wall_z1 = [284, 221, 436, 499, 446, 0, 128, 201, 62, 9, 182, 210, 159, 159, 0, 0, 500, 0]\n wall_w2 = [220, 297, 697, 157, 377, 681, 679, 49, 304, 139, 0, 480, 376, 524, 700, 0, 700, 700]\n wall_z2 = [244, 218, 478, 432, 487, 99, 98, 416, 161, 315, 126, 325, 147, 176, 0, 500, 500, 500]\n wall_rad = [1, 1, 10, 5, 20, 6, 6, 30, 30, 10, 10, 10, 10, 10, 5, 5, 5, 5]\n wall_color = [(220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220)]\n wall_RECT = [[186, 245, 37, 38], [193, 218, 107, 3], [154, 424, 555, 66], [7, 429, 157, 73], [359, 424, 356, 85], [554, -8, 135, 115], [635, 94, 52, 38], [-32, 169, 113, 279], [276, 30, 78, 163], [39, -3, 112, 330], [-8, 118, 34, 72], [472, 198, 231, 139], [368, 139, 137, 28], [485, 147, 51, 41], [-7, -7, 714, 14], [-7, -7, 14, 514], [-7, 493, 714, 14], [693, -7, 14, 514]]\n elif levely == 5:\n ball_max = 15\n ball_w = [563.2380017184845, 135.5091931534665, 435.09697027584525, 132.51126304855137, 158.80356877160969, 486.49890666361813, 28.0454597909272, 469.94449157610796, 253.77058846375945, 33.311743878553251, 651.08671805489632, 467.4560139814393, 420.90145867058521, 248.83956419449743, 98.267666685148598, 670.85536291962285]\n ball_z = [340.3499477728684, 192.53572614832325, 274.00276170743837, 474.72360924550071, 248.04392629767023, 199.66234253741388, 291.77486188629132, 98.828156873677884, 261.79870802935454, 452.90721309179793, 434.31611085503482, 422.84067516142846, 143.71750465032488, 474.55563009909457, 63.407930077910926, 97.5392796541895]\n ball_wol = [-0.12736934788998625, -0.34670289908297647, -0.62730956112551528, -0.01316352118701539, -0.36875760413492498, 0.3253705975573648, -0.43186646985168864, 0.029829055857965088, -0.051399766840351885, 0.31143213467472303, 0.91261705660387604, -0.39289683694945782, 0.6973192899270082, -0.026739395385515136, 0.47773812365404217, -0.14449244329674141]\n ball_zol = [0.2651067487506561, 0.33747092449158278, -0.20330004911815291, 0.11263669365628809, 0.62183969591811039, 0.220324713577495, 0.12382039798193512, -0.062689280803922554, 0.13756798955280808, 0.8702172500111478, -0.031277763984301599, 0.28378328194527458, 0.1666190295210413, 0.056074468995401638, 0.75422143538357722, 0.14790083350095956]\n ball_rad = [12, 20, 14, 19, 14, 23, 23, 13, 25, 28, 28, 25, 20, 20, 20, 24]\n ball_color = [(132, 202, 208), (130, 220, 228), (133, 230, 241), (133, 200, 224), (138, 244, 248), (134, 176, 212), (132, 246, 206), (136, 191, 201), (130, 247, 204), (135, 190, 248), (136, 196, 244), (137, 246, 211), (132, 176, 232), (139, 200, 204), (135, 204, 206), (137, 234, 248)]\n ball_angle = [103.32400188884675, 316.71158855283181, 66.797426175129175, 35.509394217326573, 15.886531654813545, 0.61656478963343941, 195.33151301725019, 152.08747184390086, 199.80989069184068, 131.62120808048311, 339.38767654500623, 158.21789358507957, 322.31233400906359, 97.437869538449633, 179.6312883714439, 134.41162557033078]\n ball_angleol = [0.54118695268280415, -1.0009948706990461, -0.42583251039327935, -0.049119552546591096, -1.7234897593393199, 0.1278122582140804, -0.33925087348758332, 0.98916269599321738, 0.054177225060088277, 0.93648329222661952, 2.0855948904138386, -1.2792816321392795, 1.9343475351789952, -0.094694117658838645, 1.3328174529019678, 1.0390947956294083]\n ball_squar = [[549.2380017184845, 326.3499477728684, 28, 28], [113.5091931534665, 170.53572614832325, 44, 44], [419.09697027584525, 258.00276170743837, 32, 32], [111.51126304855137, 453.72360924550071, 42, 42], [142.80356877160969, 232.04392629767023, 32, 32], [461.49890666361813, 174.66234253741388, 50, 50], [3.0454597909272003, 266.77486188629132, 50, 50], [454.94449157610796, 83.828156873677884, 30, 30], [226.77058846375945, 234.79870802935454, 54, 54], [3.3117438785532514, 422.90721309179793, 60, 60], [621.08671805489632, 404.31611085503482, 60, 60], [440.4560139814393, 395.84067516142846, 54, 54], [398.90145867058521, 121.71750465032488, 44, 44], [226.83956419449743, 452.55563009909457, 44, 44], [76.267666685148598, 41.407930077910926, 44, 44], [644.85536291962285, 71.5392796541895, 52, 52]]\n ball_mass = [5428.6721054031623, 25132.741228718347, 8620.5302414503913, 21548.184010972389, 8620.5302414503913, 38223.757816227015, 38223.757816227015, 6902.0790599367756, 49087.385212340516, 68964.24193160313, 68964.24193160313, 49087.385212340516, 25132.741228718347, 25132.741228718347, 25132.741228718347, 43429.376843225298]\n tempy = [[140.43481116747478, 230.04392629767023, 36.368757604134913, 36.621839695918112], [459.49890666361813, 172.66234253741388, 54.325370597557367, 54.220324713577497], [0.61359332107551268, 264.77486188629132, 54.431866469851684, 54.123820397981937], [452.94449157610796, 81.765467592873961, 34.029829055857967, 34.062689280803923], [224.7191886969191, 232.79870802935454, 58.051399766840348, 58.137567989552807], [1.3117438785532514, 420.90721309179793, 64.311432134674718, 64.870217250011152], [619.08671805489632, 402.28483309105053, 64.912617056603878, 64.031277763984292], [438.06311714448987, 393.84067516142846, 58.392896836949433, 58.283783281945276], [396.90145867058521, 119.71750465032488, 48.697319289927009, 48.166619029521044], [224.81282479911192, 450.55563009909457, 48.026739395385505, 48.056074468995405], [74.267666685148598, 39.407930077910926, 48.477738123654042, 48.754221435383577], [642.71087047632614, 69.5392796541895, 56.144492443296713, 56.147900833500962]]\n ball_RECT = [[547.11063237059454, 324.3499477728684, 32.127369347889953, 32.265106748750654], [111.16249025438353, 168.53572614832325, 48.34670289908297, 48.337470924491583], [416.46966071471974, 255.79946165832024, 36.627309561125514, 36.203300049118127], [109.49809952736436, 451.72360924550071, 46.01316352118701, 46.112636693656285]] + tempy\n del tempy\n wall_max = 17\n wall_type = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n wall_w1 = [135, 120, 230, 14, 531, 562, 441, 128, 403, 51, 504, 518, 377, 447, 0, 0, 0, 700]\n wall_z1 = [265, 216, 439, 499, 339, 0, 217, 104, 306, 9, 441, 210, 168, 127, 0, 0, 500, 0]\n wall_w2 = [227, 288, 697, 157, 456, 665, 476, 432, 61, 139, 633, 547, 435, 537, 700, 0, 700, 700]\n wall_z2 = [262, 200, 478, 432, 302, 141, 228, 77, 334, 315, 295, 193, 178, 114, 0, 500, 500, 500]\n wall_rad = [1, 1, 10, 5, 20, 6, 6, 30, 30, 10, 10, 10, 10, 10, 5, 5, 5, 5]\n wall_color = [(220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220)]\n wall_RECT = [[132, 262, 98, 3], [117, 201, 174, 14], [218, 427, 491, 63], [7, 429, 157, 73], [438, 284, 111, 73], [554, -8, 119, 157], [433, 209, 51, 27], [96, 49, 368, 83], [33, 274, 398, 92], [39, -3, 112, 330], [492, 287, 153, 162], [506, 185, 53, 33], [365, 156, 82, 34], [435, 106, 114, 29], [-7, -7, 714, 14], [-7, -7, 14, 514], [-7, 493, 714, 14], [693, -7, 14, 514]]\ndef draw_walls_on_big_black_rect():\n global wall_max, wall_type, wall_w1, wall_z1, wall_w2, wall_z2, wall_rad, wall_color, wall_RECT\n global big_black_rect\n global LIN_selected, CLICKER, CLICKER2\n if CLICKER:\n if LIN_selected != -1:\n nnn = LIN_selected[0]\n if LIN_selected[1] == 1:\n wall_w1[nnn] = mowse_w\n wall_z1[nnn] = mowse_z\n else:\n wall_w2[nnn] = mowse_w\n wall_z2[nnn] = mowse_z\n w1 = wall_w1[nnn]\n z1 = wall_z1[nnn]\n w2 = wall_w2[nnn]\n z2 = wall_z2[nnn]\n rad = wall_rad[nnn]\n wall_RECT[nnn] = freaky_rect_switcharoo_2D(w1 - 2, z1 - 2, w2 - w1 + 4, z2 - z1 + 4, rad)\n wl = -1\n while wl < wall_max:\n wl += 1\n w1 = wall_w1[wl]\n z1 = wall_z1[wl]\n w2 = wall_w2[wl]\n z2 = wall_z2[wl]\n rad = wall_rad[wl]\n collyu = wall_color[wl]\n pygame.draw.line(big_black_rect, collyu, (w1, z1), (w2, z2), rad * 2)\n pygame.draw.circle(big_black_rect, collyu, (w1, z1), rad)\n pygame.draw.circle(big_black_rect, collyu, (w2, z2), rad)\n #pygame.draw.rect(big_black_rect, (200, 200, 200), wall_RECT[wl], 1)\n if CLICKER2:\n if mowse_in_rect(wall_RECT[wl][0], wall_RECT[wl][1], wall_RECT[wl][2], wall_RECT[wl][3]):\n if mowse_in_circle(w1, z1, rad+3): selected = -1; LIN_selected = [wl, 1]\n elif mowse_in_circle(w2, z2, rad+3): selected = -1; LIN_selected = [wl, 2]\ndef Lets_ROLL():\n global loopy\n global ball_max, ball_w, ball_z, ball_wol, ball_zol, ball_rad, ball_color, ball_angle, ball_angleol, ball_squar, ball_mass, ball_RECT\n global wall_max, wall_type, wall_w1, wall_z1, wall_w2, wall_z2, wall_rad, wall_color, wall_RECT\n global bounce_friction, air_friction, gravity, rock_and_ROLLY\n global LIN_selected, CLICKER, CLICKER2\n global levely\n levely = 3\n bounce_friction = 0.8\n #bounce_friction = 1.0\n air_friction = 0.999\n #air_friction = 1.0\n gravity = 0.5\n rock_and_ROLLY = math.pi / 8 * 180 #24\n reset_stuff()\n fontyyy = chilly_font_Italicy(24)\n PRESS_SPACE_BAR_TO_MOVE_immy = fontyyy.render('Press SPACE BAR to start motion.', 0, (100, 200, 100))\n PRESS_SPACE_BAR_TO_STOP_immy = fontyyy.render('Press SPACE BAR to stop motion.', 0, (200, 100, 100))\n PRESS_ENTER_TO_RESET_immy = fontyyy.render('Press ENTER to reset.', 0, (150, 150, 150))\n PRESS_MINUS_TO_MINUS_immy = fontyyy.render('Press - to delete a ball.', 0, (150, 150, 150))\n PRESS_ADD_TO_ADD_immy = fontyyy.render('Press + to add a ball.', 0, (150, 150, 150))\n LEFT_CLICK_TO_immy = fontyyy.render('Left click on a \"ghost ball\" to change its speed.', 0, (150, 150, 150))\n RIGHT_CLICK_TO_immy = fontyyy.render('Right click on a ball to stop its motion.', 0, (150, 150, 150))\n PRESS_S_TO_immy = fontyyy.render('Press S to stop all balls.', 0, (150, 150, 150))\n PRESS_PAGE_UP_TO_immy = fontyyy.render('Press Page Up to change the level.', 0, (150, 150, 150))\n #message_1_immy\n del fontyyy\n #calculate_for_sure = True\n selected = -1\n LIN_selected = -1\n move_stuff = True\n t = time.time() + .01\n CLICKER = False\n CLICKER2 = False\n loopy = 1\n while loopy:\n big_black_rect.fill((0, 0, 0))\n draw_walls_on_big_black_rect()\n screen.blit(big_black_rect, (0, 0))\n check_for_keys()\n CLICKER = mowse_left_held\n CLICKER2 = mowse_left_pressed\n CLICKER_2 = mowse_right_held\n CLICKER2_2 = mowse_right_pressed\n if ky_first_held_CEV(32): move_stuff = not move_stuff\n if ky_first_held_CEV(13): reset_stuff()\n if ky_first_held_CEV(280):\n levely += 1\n if levely > 5: levely = 1\n reset_stuff()\n if ky_first_held_CEV(115): # S\n M = -1\n while M < ball_max:\n M += 1\n ball_wol[M] = 0\n ball_zol[M] = 0\n updatey_ball_quick_rect(M)\n if ky_first_held_CEV(45) or ky_first_held_CEV(269): # -\n minus_ball_thing(0)\n if ky_first_held_CEV(61) or ky_first_held_CEV(270): # +\n add_ball_thing(350 + randy(40, -20), 400 + randy(40, -20), randy(40, -20), randy(40, -20), int_randy(20, 10), (int_randy(10, 130), int_randy(80, 170), int_randy(50, 200)), 0, 0, True, True)\n if ky_first_held_CEV(49):\n listy = ['Level_save']\n listy += ['ball_max = ' + str(ball_max)]\n listy += ['ball_w = ' + str(ball_w)]\n listy += ['ball_z = ' + str(ball_z)]\n listy += ['ball_wol = ' + str(ball_wol)]\n listy += ['ball_zol = ' + str(ball_zol)]\n listy += ['ball_rad = ' + str(ball_rad)]\n listy += ['ball_color = ' + str(ball_color)]\n listy += ['ball_angle = ' + str(ball_angle)]\n listy += ['ball_angleol = ' + str(ball_angleol)]\n listy += ['ball_squar = ' + str(ball_squar)]\n listy += ['ball_mass = ' + str(ball_mass)]\n listy += ['ball_RECT = ' + str(ball_RECT)]\n listy += ['wall_max = ' + str(wall_max)]\n listy += ['wall_type = ' + str(wall_type)]\n listy += ['wall_w1 = ' + str(wall_w1)]\n listy += ['wall_z1 = ' + str(wall_z1)]\n listy += ['wall_w2 = ' + str(wall_w2)]\n listy += ['wall_z2 = ' + str(wall_z2)]\n listy += ['wall_rad = ' + str(wall_rad)]\n listy += ['wall_color = ' + str(wall_color)]\n listy += ['wall_RECT = ' + str(wall_RECT)]\n ##write_to_file_WEEE_STRANGE(\"Level_Save.dat\", listy)\n del listy\n if CLICKER2:\n allow_selectey_thing = True\n else:\n allow_selectey_thing = False\n if not CLICKER:\n selected = -1\n LIN_selected = -1\n to_be_selected = selected\n M = -1\n while M < ball_max:\n M += 1\n if move_stuff:\n move_ball(M)\n wwol = int(ball_w[M] + ball_wol[M])\n zzol = int(ball_z[M] + ball_zol[M])\n pygame.draw.circle(screen, ball_color[M], (int(ball_w[M]), int(ball_z[M])), ball_rad[M])\n blpw, blpz = point_rotated_by_angle_2D(0, -ball_rad[M], 0, 0, ball_angle[M])\n pygame.draw.line(screen, (100, 100, 100), (int(ball_w[M] + blpw), int(ball_z[M] + blpz)), (int(ball_w[M]), int(ball_z[M])))\n if not move_stuff:\n pygame.draw.circle(screen, (100, 100, 250), (wwol, zzol), ball_rad[M], 1)\n pygame.draw.circle(screen, (100, 100, 150), (wwol, zzol), int(ball_rad[M] * 1.0), 1)\n pygame.draw.circle(screen, (150, 150, 200), (wwol, zzol), int(ball_rad[M] * 0.8), 1)\n pygame.draw.circle(screen, (200, 200, 250), (wwol, zzol), int(ball_rad[M] * 0.5), 1)\n pygame.draw.line(screen, (100, 160, 250), (int(ball_w[M]), int(ball_z[M])), (wwol, zzol))\n pygame.draw.rect(screen, (130, 130, 130), ball_RECT[M], 1)\n pygame.draw.rect(screen, (140, 140, 140), ball_squar[M], 1)\n if allow_selectey_thing:\n if mowse_in_rect(ball_RECT[M][0], ball_RECT[M][1], ball_RECT[M][2], ball_RECT[M][3]):\n if mowse_in_circle(wwol, zzol, ball_rad[M]):\n to_be_selected = M\n LIN_selected = -1\n if CLICKER_2:\n if mowse_in_rect(ball_squar[M][0], ball_squar[M][1], ball_squar[M][2], ball_squar[M][3]):\n if mowse_in_circle(ball_w[M], ball_z[M], ball_rad[M]):\n ball_wol[M] = 0\n ball_zol[M] = 0\n ball_angleol[M] = 0\n updatey_ball_quick_rect(M)\n if CLICKER:\n if selected == M:\n if move_stuff:\n mowseyy_w = mowse_w\n mowseyy_z = mowse_z\n bw1 = ball_rad[M]\n bz1 = ball_rad[M]\n bw2 = APPLICATION_w_size - ball_rad[M]\n bz2 = APPLICATION_z_size - ball_rad[M]\n if mowseyy_w < bw1: mowseyy_w = bw1\n if mowseyy_w > bw2: mowseyy_w = bw2\n if mowseyy_z < bz1: mowseyy_z = bz1\n if mowseyy_z > bz2: mowseyy_z = bz2\n ww = mowseyy_w - ball_w[M]\n zz = mowseyy_z - ball_z[M]\n #dissy = distance_2D(0, 0, ww, zz)\n ball_wol[M] = ww # / 2.0 # / dissy\n ball_zol[M] = zz # / 2.0 # / dissy\n else:\n ball_wol[M] = mowse_w - ball_w[M]\n ball_zol[M] = mowse_z - ball_z[M]\n updatey_ball_quick_rect(M)\n selected = to_be_selected\n if not move_stuff:\n screen.blit(PRESS_SPACE_BAR_TO_MOVE_immy, (10, 10))\n else:\n screen.blit(PRESS_SPACE_BAR_TO_STOP_immy, (10, 10))\n screen.blit(PRESS_MINUS_TO_MINUS_immy, (10, 30))\n screen.blit(PRESS_ADD_TO_ADD_immy, (10, 50))\n screen.blit(PRESS_ENTER_TO_RESET_immy, (10, 70))\n screen.blit(LEFT_CLICK_TO_immy, (10, 90))\n screen.blit(RIGHT_CLICK_TO_immy, (10, 110))\n screen.blit(PRESS_S_TO_immy, (10, 130))\n screen.blit(PRESS_PAGE_UP_TO_immy, (10, 150))\n pygame.display.flip()\n while t > time.time(): pass\n t = time.time() + .01\n # Try_Again_HE_HE Is weird!! maybe It should be deleted!!\ndef move_ball(M):\n ball_angle[M] += ball_angleol[M]\n if ball_angle[M] > 359: ball_angle[M] -= 360\n elif ball_angle[M] < 0: ball_angle[M] += 361\n #movey_bally_speciality(M, ball_wol[M], ball_zol[M], 10)\n movey_bally_speciality(M, ball_wol[M], ball_zol[M], 10)\n ball_zol[M] += gravity\n updatey_ball_quick_rect(M)\ndef movey_bally_speciality(M, wol_special, zol_special, Try_Again_HE_HE):\n global loopy\n global ball_max, ball_w, ball_z, ball_wol, ball_zol, ball_rad, ball_color, ball_angle, ball_angleol, ball_squar, ball_mass, ball_RECT\n global wall_max, wall_type, wall_w1, wall_z1, wall_w2, wall_z2, wall_rad, wall_color, wall_RECT\n global bounce_friction, air_friction, gravity, rock_and_ROLLY\n distance_is_supposed_to_be_at = distance_2D(0, 0, wol_special, zol_special)\n wa = ball_w[M]\n za = ball_z[M]\n #will_be_w = wa + ball_wol[M]\n #will_be_z = za + ball_zol[M]\n will_be_w = wa + wol_special\n will_be_z = za + zol_special\n LIN_collide_max = -1\n LIN_collide_w = []\n LIN_collide_z = []\n LIN_collide_ang = []\n LIN_collide_dis = []\n LL = -1\n while LL < wall_max:\n LL += 1\n if rect_touching_rect2(ball_RECT[M][0], ball_RECT[M][1], ball_RECT[M][2], ball_RECT[M][3], wall_RECT[LL][0], wall_RECT[LL][1], wall_RECT[LL][2], wall_RECT[LL][3]):\n #print 'weee'\n did_collide, New_ball_w, New_ball_z, angle_hit_at = find_where_ball_collides_on_a_wall(wa, za, wol_special, zol_special, ball_rad[M], wall_type[LL], wall_w1[LL], wall_z1[LL], wall_w2[LL], wall_z2[LL], wall_rad[LL])\n if did_collide:\n #print 'collide'\n #print str(New_ball_w), str(New_ball_z)\n LIN_collide_max += 1\n LIN_collide_w += [New_ball_w]\n LIN_collide_z += [New_ball_z]\n LIN_collide_ang += [angle_hit_at]\n LIN_collide_dis += [distance_2D(wa, za, New_ball_w, New_ball_z)]\n HEH_collide_max = -1\n HEH_collide_w = []\n HEH_collide_z = []\n HEH_collide_ang = []\n HEH_collide_dis = []\n HEH_collide_ball_hit = []\n M2 = -1\n while M2 < ball_max:\n M2 += 1\n if M2 != M:\n if rect_touching_rect2(ball_RECT[M][0], ball_RECT[M][1], ball_RECT[M][2], ball_RECT[M][3], ball_squar[M2][0], ball_squar[M2][1], ball_squar[M2][2], ball_squar[M2][3]):\n #they_did_touch, New_ball1_w, New_ball1_z, angle_hit_at = find_where_ball_collides_on_another_ball(wa, za, ball_wol[M], ball_zol[M], ball_rad[M], ball_w[M2], ball_z[M2], ball_rad[M2])\n they_did_touch, New_ball1_w, New_ball1_z, angle_hit_at = find_where_ball_collides_on_another_ball(wa, za, wol_special, zol_special, ball_rad[M], ball_w[M2], ball_z[M2], ball_rad[M2])\n if they_did_touch:\n HEH_collide_max += 1\n HEH_collide_w += [New_ball1_w]\n HEH_collide_z += [New_ball1_z]\n HEH_collide_ang += [angle_hit_at]\n HEH_collide_dis += [distance_2D(wa, za, New_ball1_w, New_ball1_z)]\n HEH_collide_ball_hit += [M2]\n current_dis = distance_is_supposed_to_be_at\n Wall_to_hit_at_angley = None\n Grr = -1\n while Grr < LIN_collide_max:\n Grr += 1\n #print LIN_collide_dis[Grr], current_dis\n if LIN_collide_dis[Grr] < current_dis:\n #print 'weee!'\n Wall_to_hit_at_angley = LIN_collide_ang[Grr]\n current_dis = LIN_collide_dis[Grr]\n will_be_w = LIN_collide_w[Grr]\n will_be_z = LIN_collide_z[Grr]\n Ball_to_hit = None\n Ball_to_hit_at_angley = None\n Heh = -1\n while Heh < HEH_collide_max:\n Heh += 1\n if HEH_collide_dis[Heh] < current_dis:\n if ball_is_going_towards_ball(M, HEH_collide_ball_hit[Heh]):\n if ball_is_relatively_going_towards_ball(M, HEH_collide_ball_hit[Heh]):\n Ball_to_hit = HEH_collide_ball_hit[Heh]\n Ball_to_hit_at_angley = HEH_collide_ang[Heh]\n else:\n Ball_to_hit = None\n Ball_to_hit_at_angley = None\n current_dis = HEH_collide_dis[Heh]\n will_be_w = HEH_collide_w[Heh]\n will_be_z = HEH_collide_z[Heh]\n if Ball_to_hit != None:\n Make_two_balls_hit_at_angle(M, Ball_to_hit, Ball_to_hit_at_angley)\n else:\n #if bouncey == 1: ball_wol[M] = -ball_wol[M] * bounce_friction\n #elif bouncey == 2: ball_zol[M] = -ball_zol[M] * bounce_friction\n if Wall_to_hit_at_angley != None:\n ball_wol[M], ball_zol[M] = wzol_bounce_at_angle(ball_wol[M], ball_zol[M], Wall_to_hit_at_angley, bounce_friction)\n ball_angleol[M] = zol_at_angle(ball_wol[M], ball_zol[M], Wall_to_hit_at_angley + 90) / ball_rad[M] * rock_and_ROLLY\n ball_w[M] = will_be_w\n ball_z[M] = will_be_z\n if ball_w[M] < 0 or ball_w[M] > APPLICATION_w_size or ball_z[M] < 0 or ball_z[M] > APPLICATION_z_size:\n #print str(M) + \" \", str(wa), str(za)\n print str(M) + \" \", str(ball_w[M]), str(ball_z[M]), str(ball_rad[M])\n ball_wol[M] *= air_friction\n ball_zol[M] *= air_friction\n updatey_ball_quick_rect(M)\n if current_dis < distance_is_supposed_to_be_at:\n if Try_Again_HE_HE > 0:\n distance_to_travel_next = distance_is_supposed_to_be_at - current_dis\n disy_HE_HE = distance_2D(0, 0, ball_wol[M], ball_zol[M])\n next_wol = ball_wol[M]\n next_zol = ball_zol[M]\n movey_bally_speciality(M, next_wol, next_zol, Try_Again_HE_HE - 1)\n\n ## Woah... Finally! Were near the end of the program! ##\nif __name__ == '__main__':\n import math\n import pygame\n import random\n import time\n import gc\n import copy\n from pygame.locals import *\n if not pygame.font: print 'Warning, fonts disabled?'\n if not pygame.mixer: print 'Warning, sound disabled?'\n HE_HE_init()\n ## THE END! ##" + }, + { + "link" : "Rect.collidepoint", + "user_title" : "Anonymous", + "content" : "Guillame was confused on the nature of the last two arguments to Rect().\nhe thought that they were absolute coordinates, not width-height.\nThere isn't a bug with collidepoint.\nsee the pygame mailing list archives for the discussion with Guillame where this topic arose.", + "id" : 524, + "datetimeon" : "2007-04-25T17:52:17" + }, + { + "datetimeon" : "2007-04-26T17:03:55", + "id" : 528, + "content" : "I'm guessing they're key pressed (eg, the A or J keys) and the modifiers (Shift, Ctrl, Alt, Meta, Super, etc). Experiment to find details.", + "user_title" : "Anonymous", + "link" : "pygame.event" + }, + { + "datetimeon" : "2008-01-20T04:04:36", + "id" : 1428, + "user_title" : "Anonymous", + "content" : "I, too, get a list with a single item, None. This on Mac OS X 10.4 (Tiger) and Python 2.4.4; \npygame.ver returns '1.8.0pre'", + "link" : "pygame.font.get_fonts" + }, + { + "content" : "The params units are in pixels.\nThe smallest unit for Pygame I think.\nIt doesn't make sense to change this to float.", + "user_title" : "Anonymous", + "link" : "Rect.move_ip", + "datetimeon" : "2008-01-21T10:48:00", + "id" : 1430 + }, + { + "id" : 1431, + "datetimeon" : "2008-01-21T20:04:03", + "link" : "pygame.key.name", + "user_title" : "Anonymous", + "content" : "this resource is perfect for games with controls options\nbecause through it you can show the current input without creating a whole database of inputs\ntxt = font.render(pygame.key.name(current_key),True,(0,0,0))\nscreen.blit(txt,(0,0))" + }, + { + "content" : "#!/usr/bin/python\nimport pygame\nfrom pygame.locals import *\n\ndef main():\n pygame.init()\n pygame.display.set_mode((300,200))\n pygame.display.set_caption('Testing')\n running = True\n while running:\n for event in pygame.event.get():\n if event.type == QUIT:\n running = False\n if event.type == KEYDOWN and event.key == K_ESCAPE:\n running = False\n if event.type == MOUSEBUTTONDOWN:\n #print event.button\n print pygame.mouse.get_pos()\n pygame.display.quit()\n\nif __name__ == '__main__':\n main()", + "user_title" : "Anonymous", + "link" : "pygame.mouse.get_pos", + "datetimeon" : "2008-01-23T15:43:49", + "id" : 1436 + }, + { + "datetimeon" : "2007-05-04T00:39:15", + "id" : 548, + "user_title" : "Anonymous", + "content" : "Once you draw something, how do you delete it??", + "link" : "pygame.draw.rect" + }, + { + "datetimeon" : "2007-05-09T10:27:03", + "id" : 554, + "content" : "Hi,\n\nI was having a problem when trying to jump from one movie to another. Even with the fist one stopped the second movie played at half speed with no volume. A solution was to keep a reference to the old movie, which I assume stops the garbage collector trying to delete it before its finished doing whatever it was doing. \n\nThis would cause problems:\n\ncurrentMovie.stop()\ncurrentMovie = pygame.movie.Movie(fullname)\ncurrentMovie.play()\n\nAnd this fixes it:\n\ncurrentMovie.stop()\noldMovie = currentMovie\ncurrentMovie = pygame.movie.Movie(fullname)\ncurrentMovie.play()\n\nMaybe need a method for unloading the memory and stopping all its threads.", + "user_title" : "Anonymous", + "link" : "pygame.movie" + }, + { + "id" : 1456, + "datetimeon" : "2008-01-29T10:33:27", + "link" : "pygame.display.set_mode", + "user_title" : "Anonymous", + "content" : "Does anyone know how to make multiple screens? I've tried making a different variable with different dimensions, but it only changes original\n\nsize=400,400\nscreen=display.set_mode(size)\n\nsize1=200,200\nnew_screen=display.set_mode(size1)" + }, + { + "link" : "Surface.blit", + "user_title" : "Anonymous", + "content" : "The first two arguments to Surface.blit() seem to be reversed. To draw Surface \"source\" onto Surface \"dest\" the correct call is:\n\n pygame.Surface.blit(dest, source, position)\n\nor\n\n dest.blit(source, position)\n\n\nExample:\n\n screen = pygame.display.set_mode((100,100))\n screen.fill((255,255,255)) # white background\n red_block = pygame.Surface((50,50))\n red_block.fill((255,0,0))\n\n # draw red block onto white background\n screen.blit(red_block, (25,25))\n\n pygame.display.update()", + "id" : 572, + "datetimeon" : "2007-05-16T08:51:09" + }, + { + "id" : 573, + "datetimeon" : "2007-05-16T09:15:43", + "link" : "Surface.blit", + "user_title" : "Anonymous", + "content" : "Actually, on re-reading the method description, I realize that the \"dest\" argument means the position to where the source Surface should be copied too. So the call synopsis is equivalent to the second case in my comment.\n\nThe naming of the arguments is a bit confusing, IMHO, and also that it is not (visually) clear that the documentation describes the methods of a Surface object instance and makes no mention of the class methods." + }, + { + "datetimeon" : "2007-05-20T19:04:19", + "id" : 578, + "content" : "Only the \"systems with multiple choices\" are listed there.", + "user_title" : "Anonymous", + "link" : "pygame.display.init" + }, + { + "link" : "Surface.blit", + "content" : "Yeah the doc could be improved. The wrong comment below should be removed, it's only confusing. I did find another problem, though: It's important to note that when you cut out an area from the source, then the dest argument does _not_ specify where the origin of the source would be on the surface that is blitted on, but instead dest specifies the top left corner of just the area that is actually blitted. This is not quite clear from this doc, I think.", + "user_title" : "Anonymous", + "id" : 588, + "datetimeon" : "2007-05-24T07:20:10" + }, + { + "user_title" : "Anonymous", + "content" : "Pygame (and SDL) doesn't support multiple windows.", + "link" : "pygame.display.set_mode", + "datetimeon" : "2008-02-09T15:29:56", + "id" : 1525 + }, + { + "user_title" : "Anonymous", + "content" : "you can't \"delete\" something you have drawn, you have to draw something over it instead.\nyou can make classes that wrap the different draw function and have both a \"show\" and a \"hide\" function and the hide function has to draw the background over the shape you created in \"hide\", but this can have weird results if shapes overlap and are not correctly redrawn.\ncheers", + "link" : "pygame.draw.rect", + "datetimeon" : "2007-06-02T10:44:48", + "id" : 607 + }, + { + "content" : "destination.blit(source (distination location),(source location x,y and size x,y)", + "user_title" : "Anonymous", + "link" : "pygame.Surface", + "datetimeon" : "2007-06-05T09:13:28", + "id" : 609 + }, + { + "id" : 610, + "datetimeon" : "2007-06-05T09:13:56", + "link" : "Surface.blit", + "user_title" : "Anonymous", + "content" : "destination.blit(source (distination location),(source location x,y and size x,y)" + }, + { + "content" : "use events to save and release keystates to use it only for some:\n\nif event.type == pygame.KEYDOWN: if event.key == MYKEY: i = True\nelif event.type == pygame.KEYUP: if event.key == MYKEY: i = False\nif i: do stuff.", + "user_title" : "Anonymous", + "link" : "pygame.key.set_repeat", + "datetimeon" : "2008-02-11T16:09:33", + "id" : 1541 + }, + { + "content" : "Is there a way to make the collision box of the line accurate to the line itself?", + "user_title" : "Anonymous", + "link" : "pygame.draw.line", + "datetimeon" : "2008-02-12T18:09:15", + "id" : 1549 + }, + { + "datetimeon" : "2008-02-12T18:13:18", + "id" : 1550, + "content" : "Is there a way to produce small line segments that have accurate collision boxes\nso that one could have two lines that would be parallel to each other with out them colliding?", + "user_title" : "Anonymous", + "link" : "pygame.draw" + }, + { + "id" : 1552, + "datetimeon" : "2008-02-12T20:21:41", + "link" : "Rect.move_ip", + "user_title" : "Anonymous", + "content" : "Yes, actually it would be quite useful for it to be float, if it could store the\ndecimals and increment them as such but not display it until it would take effect..." + }, + { + "datetimeon" : "2007-06-10T10:28:36", + "id" : 628, + "user_title" : "Anonymous", + "content" : "Clearly, more documentation about the event properties should be written.", + "link" : "pygame.event" + }, + { + "link" : "pygame.event", + "user_title" : "Anonymous", + "content" : "MOUSEBUTTONDOWN/UP:\n pos: tuple of x and y coordinates of the click\n button: 1 - left 2 - middle 3 - right button", + "id" : 630, + "datetimeon" : "2007-06-10T13:16:50" + }, + { + "id" : 631, + "datetimeon" : "2007-06-10T13:18:03", + "link" : "pygame.event", + "user_title" : "Anonymous", + "content" : "MOUSEMOTION\n pos: tuple of x and y coordinates\n rel: tuple of relative x and relative y change from previous position\n buttons: tuple of three values (left,middle,right). 0-not pressed 1-pressed" + }, + { + "datetimeon" : "2008-02-16T15:39:17", + "id" : 1580, + "user_title" : "Anonymous", + "content" : "While thick arcs do get filled, they also get moire holes - at least on Debian's 1.7.1release-4.1. For now, I've been using a rather ugly workaround where one draws the arc several times with the start angle offset by 0.01 to cut the moires back.", + "link" : "pygame.draw.arc" + }, + { + "link" : "Joystick.get_axis", + "content" : "I have noticed that with my analog joystick square shaped information is returned\nfrom this function. For example, pressing fully down and right would return 1.0\nfor both axis directions, instead of 0.7071... as one might expect (since analog\njoysticks have a circle shaped socket for the stick to move in.)\n\nTo correct this one might want to use a function similar to the following\njoystick_transform function:\n\ndef length(v):\n\treturn math.sqrt(v[0]*v[0] + v[1]*v[1])\n\n# Transforms the square info of an analog joystick into circular info\ndef joystick_transform(j):\n\t# If joystick is not centered:\n\tif (j[0],j[1]) != (0,0):\n\t\t# Check if x axis is larger than y axis\n\t\tif abs(j[0]) > abs(j[1]):\n\t\t\t# Since x>y we will check for line intersection with wall\n\t\t\t# Get slope (m = y/x) for y = m * x (line equation)\n\t\t\tm = abs(j[1] / j[0])\n\t\t\t# At x=1.0 (intersecting right wall), y would equal m\n\t\t\t# scaler = length of normalized vector / length of line intersecting box\n\t\t\ts = 1.0 / length((1.0, m))\n\t\telse:\n\t\t\t# Since y>=x we will check for line intersection with ceiling\n\t\t\t# Get slope (m = x/y) for x = m * y (line equation)\n\t\t\tm = abs(j[0] / j[1])\n\t\t\t# At y=1.0 (intersecting ceiling), x would equal m\n\t\t\t# scaler = length of normalized vector / length of line intersecting box\n\t\t\ts = 1.0 / length((m,1.0))\n\telse:\n\t\t# Since the joystick is centered, the scaler will be 0\n\t\ts = 0\n\t\t\n\t# Simply scale the joystick axis data by the scaler\n\treturn (j[0] * s, j[1] * s)\n\n-----\n\nHere is a full example illustrating the difference between raw joystick input\nand transformed joystick information:\n\n#!/usr/bin/python\n\n# In this example the function joystick_transform will transform the\n# square shaped joystick axis information into a circular shape.\n# This will make the new joystick axis information easier to use while\n# moving around a character or cursor.\n\n# The RED dot represents the actual joystick information.\n\n# The BLUE dot represents the transformed joystick information.\n\n# The GREEN dot is a cursor that moves by the transformed joystick\n# information in a motion relative to it's previous location.\n\n\nimport pygame\nimport math\n\ndef norm(v):\n\tl = length(v)\n\tif l != 0:\n\t\treturn (v[0] / l, v[1] / l)\n\treturn (0,0)\n\t\ndef length(v):\n\treturn math.sqrt(v[0]*v[0] + v[1]*v[1])\n\n# Transforms the square info of an analog joystick into circular info\ndef joystick_transform(j):\n\t# If joystick is not centered:\n\tif (j[0],j[1]) != (0,0):\n\t\t# Check if x axis is larger than y axis\n\t\tif abs(j[0]) > abs(j[1]):\n\t\t\t# Since x>y we will check for line intersection with wall\n\t\t\t# Get slope (m = y/x) for y = m * x (line equation)\n\t\t\tm = abs(j[1] / j[0])\n\t\t\t# At x=1.0 (intersecting right wall), y would equal m\n\t\t\t# scaler = length of normalized vector / length of line intersecting box\n\t\t\ts = 1.0 / length((1.0, m))\n\t\telse:\n\t\t\t# Since y>=x we will check for line intersection with ceiling\n\t\t\t# Get slope (m = x/y) for x = m * y (line equation)\n\t\t\tm = abs(j[0] / j[1])\n\t\t\t# At y=1.0 (intersecting ceiling), x would equal m\n\t\t\t# scaler = length of normalized vector / length of line intersecting box\n\t\t\ts = 1.0 / length((m,1.0))\n\telse:\n\t\t# Since the joystick is centered, the scaler will be 0\n\t\ts = 0\n\t\t\n\t# Simply scale the joystick axis data by the scaler\n\treturn (j[0] * s, j[1] * s)\n\npygame.init()\npygame.joystick.init()\n\nscreen = pygame.display.set_mode((640,480))\n\njs = pygame.joystick.Joystick(0)\njs.init()\n\npx = 320.0\npy = 240.0\n\nmove_speed = 2.0\n\ndone = False\nwhile not done:\n\tkey = pygame.key.get_pressed()\n\tscreen.fill((255,255,255))\n\t\n\t# Outer box boundry\n\tpygame.draw.rect(screen, (200,200,200), ((10,10),(180,180)), 1)\n\t\n\t# Circle boundry\n\tpygame.draw.circle(screen, (0,0,0), (100,100), 90, 1)\n\t\n\t# Center point\n\tpygame.draw.circle(screen, (200,200,200), (100,100), 2, 1)\n\t\n\tjx = js.get_axis(0)\n\tjy = js.get_axis(1)\n\tn = norm((jx,jy))\n\t\n\t# Line representing normalized joystick information\n\tpygame.draw.line(screen, (200,200,200), (100,100), (100 + int(n[0] * 90.0), 100 + int(n[1] * 90.0)))\n\t\n\t# Raw joystick information\n\tx = 100 + int(jx * 90.0)\n\ty = 100 + int(jy * 90.0)\n\tpygame.draw.circle(screen, (255,0,0), (x, y), 5)\n\t\n\t# Transformed joystick information\n\ttj = joystick_transform((jx,jy))\n\tx = 100 + int(tj[0] * 90.0)\n\ty = 100 + int(tj[1] * 90.0)\n\tpygame.draw.circle(screen, (0,0,255), (x, y), 5)\n\t\n\t# Cursor moved by transformed joystick information\n\tpx = px + tj[0] * move_speed\n\tpy = py + tj[1] * move_speed\n\tpygame.draw.circle(screen, (0, 255, 0), (int(px), int(py)), 5)\n\t\n\tpygame.display.flip()\n\t\n\tfor event in pygame.event.get():\n\t\tif event.type == pygame.QUIT: \n\t\t\tdone = True\n\t\t\t\n\t\tif key[pygame.K_ESCAPE]:\n\t\t\tdone = True", + "user_title" : "Anonymous", + "id" : 1584, + "datetimeon" : "2008-02-16T23:25:56" + }, + { + "link" : "pygame.mixer.music.play", + "content" : "the playback doesnt work, the music stops for a second and then starts again\nHELP ME", + "user_title" : "Anonymous", + "id" : 3415, + "datetimeon" : "2010-11-27T14:29:59" + }, + { + "datetimeon" : "2008-02-27T10:13:22", + "id" : 1657, + "content" : "An example of the Color object is an rgb tuple like (100,0,200).", + "user_title" : "Anonymous", + "link" : "Surface.set_at" + }, + { + "link" : "pygame.display.init", + "user_title" : "Anonymous", + "content" : "It might be convenient to have this particular documentation within the\nhelp(pygame.display.init) documentation, as of this writing it is not.\n\nAdditionally, information pertaining to Mac-OS X is not present; it may also be\nnoteworthy to document the methods by which Pygame renders its surfaces, as OSX,\nlike Windows, has its own various subsystems to draw views(surfaces).", + "id" : 1680, + "datetimeon" : "2008-03-01T23:13:53" + }, + { + "datetimeon" : "2008-03-11T11:07:08", + "id" : 1682, + "user_title" : "Anonymous", + "content" : "All the keyboard event.key constants:\n\nLetters:\n K_a ... K_z\n\nNumbers:\n K_0 ... K_9\n\nControl:\n K_TAB\n K_RETURN\n K_ESCAPE\n K_SCROLLOCK\n K_SYSREQ\n K_BREAK\n K_DELETE\n K_BACKSPACE\n K_CAPSLOCK\n K_CLEAR\n K_NUMLOCK\n\nPunctuation:\n K_SPACE\n K_PERIOD\n K_COMMA\n K_QUESTION\n K_AMPERSAND\n K_ASTERISK\n K_AT\n K_CARET\n K_BACKQUOTE\n K_DOLLAR\n K_EQUALS\n K_EURO\n K_EXCLAIM\n K_SLASH, K_BACKSLASH\n K_COLON, K_SEMICOLON\n K_QUOTE, K_QUOTEDBL\n K_MINUS, K_PLUS\n K_GREATER, K_LESS\n\nBrackets:\n K_RIGHTBRACKET, K_LEFTBRACKET\n K_RIGHTPAREN, K_LEFTPAREN\n\nF-Keys:\n K_F1 ... K_F15\n\nEdit keys:\n K_HELP\n K_HOME\n K_END\n K_INSERT\n K_PRINT\n K_PAGEUP, K_PAGEDOWN\n K_FIRST, K_LAST\n\nKeypad:\n K_KP0 ... K_KP9\n K_KP_DIVIDE\n K_KP_ENTER\n K_KP_EQUALS\n K_KP_MINUS\n K_KP_MULTIPLY\n K_KP_PERIOD\n K_KP_PLUS\n\nSHF,CTL,ALT etc:\n K_LALT, K_RALT\n K_LCTRL, K_RCTRL\n K_LSUPER, K_RSUPER\n K_LSHIFT, K_RSHIFT\n K_RMETA, K_LMETA\n\nArrows:\n K_LEFT\n K_UP\n K_RIGHT\n K_DOWN\n\nOther:\n K_MENU\n K_MODE\n K_PAUSE\n K_POWER\n K_UNDERSCORE\n K_HASH\n\n K_UNKNOWN", + "link" : "pygame.event.Event" + }, + { + "datetimeon" : "2008-03-16T02:36:10", + "id" : 1685, + "user_title" : "Anonymous", + "content" : "key is one of the K_* constants in the pygame package level -- it indicates the key pressed. For example, K_UP or K_ESCAPE.\nmod is the modifier. I'm assuming it's either a bitfield or a list. Shouldn't be hard to figure it out.", + "link" : "pygame.event" + }, + { + "datetimeon" : "2007-07-12T09:58:26", + "id" : 716, + "content" : "can anyone give me just a small script about how can i play a movie, please?\n\ni used this script but nothing happened. just a window but blank :(\n\nimport pygame\n\n\npygame.init()\n\n\ncine = pygame.movie.Movie('film.mpg')\nsz=cine.get_size()\npygame.display.set_mode(sz)\nwhile 1:\n cine.play(1)\n\nthe movie loads because i tried to find the length and worked and the movie is at the same location. please HELP!!", + "user_title" : "Anonymous", + "link" : "pygame.movie" + }, + { + "id" : 719, + "datetimeon" : "2007-07-14T15:07:03", + "link" : "pygame.event", + "content" : "JOYBUTTONDOWN/JOYBUTTONUP\nbutton -- the ID of the button which fired the event.", + "user_title" : "Anonymous" + }, + { + "id" : 1713, + "datetimeon" : "2008-03-28T14:12:55", + "link" : "pygame.draw.aaline", + "content" : "Is there a way to draw an anti aliased line with a thickness?", + "user_title" : "Anonymous" + }, + { + "user_title" : "Anonymous", + "content" : "I've notice that passing any negative number will cause the music to loop forever, not just -1.", + "link" : "pygame.mixer.music.play", + "datetimeon" : "2008-03-30T20:55:44", + "id" : 1722 + }, + { + "user_title" : "Anonymous", + "content" : "It seams that you can't read the axes or button positions if you don't start the event loop.\nThis is a little different from what the docs are saying but actually expected since the joystick \nbroadcasts the position (is this right for all drivers?)", + "link" : "pygame.joystick.Joystick", + "datetimeon" : "2008-04-02T23:40:29", + "id" : 1727 + }, + { + "datetimeon" : "2007-07-16T21:15:36", + "id" : 726, + "content" : "Can I draw just one pixel with this? \nApparantly the smallest rect one can draw is 2 pixels big, I guess?", + "user_title" : "Anonymous", + "link" : "pygame.draw.rect" + }, + { + "content" : "the fade_in parameter seems to be missing. Using 1.7: \nTypeError: function takes at most 2 arguments (3 given)", + "user_title" : "Anonymous", + "link" : "Sound.play", + "datetimeon" : "2008-04-04T23:21:49", + "id" : 1732 + }, + { + "link" : "pygame.transform.smoothscale", + "content" : "The algorithm used will probably ruin the edges in your images. Makes them kinda blurry.", + "user_title" : "Anonymous", + "id" : 1734, + "datetimeon" : "2008-04-05T17:52:43" + }, + { + "datetimeon" : "2007-07-18T19:04:17", + "id" : 733, + "user_title" : "Anonymous", + "content" : "", + "link" : "Very cool design! Useful information. Go on!" + }, + { + "id" : 734, + "datetimeon" : "2007-07-18T19:04:17", + "link" : "Pretty nice site, wants to see much more on it! :)", + "user_title" : "Anonymous", + "content" : "" + }, + { + "content" : "", + "user_title" : "Anonymous", + "link" : "Thanks for the enjoy to have you on my site ! Good luck.", + "datetimeon" : "2007-07-18T19:04:17", + "id" : 735 + }, + { + "user_title" : "Anonymous", + "content" : "How about creating a sound from a string of raw samples? I shouldn't have to construct a fake WAV header just to get Pygame to accept sound data.", + "link" : "pygame.mixer.Sound", + "datetimeon" : "2008-04-09T23:10:47", + "id" : 1742 + }, + { + "link" : "pygame.event", + "user_title" : "Anonymous", + "content" : "Here are attributes of the different events (as best I can tell):\n\nACTIVEEVENT:\n\tgain\n\tstate\nKEYDOWN:\n\tunicode\nKEYUP:\n\tkey\n\tmod\nMOUSEMOTION:\n\tpos\n\trel\n\tbuttons\nMOUSEBUTTONDOWN and MOUSEBUTTONUP:\n\tpos\n\tbutton\nJOYAXISMOTION:\n\tjoy\n\taxis\n\tvalue\nJOYBALLMOTION:\n\tjoy\n\tball\n\trel\nJOYHATMOTION:\n\tjoy\n\that\n\tvalue\nJOYBUTTONUP and case JOYBUTTONDOWN:\n\tjoy\n\tbutton\nVIDEORESIZE:\n\tsize\n\tw\n\th\nSYSWMEVENT (WIN32 only):\n\thwnd\n\tmsg\n\twparam\n\tlparam", + "id" : 740, + "datetimeon" : "2007-07-19T13:22:47" + }, + { + "link" : "Font.render", + "user_title" : "Anonymous", + "content" : "> It is true that passing None for the final argument causes \"Invalid RGBA argument\". This is a bug in the documentation, not the code.\nI'd suggest replacing 'None' with 'NULL' in the documentation, then - it also indicates no value, but is not one you can enter in Python (hence there's no confusion).", + "id" : 741, + "datetimeon" : "2007-07-19T16:32:03" + }, + { + "link" : "pygame.Surface", + "user_title" : "Anonymous", + "content" : "There seems to be a typo in the sentence \"The blit routines will attempt to use hardware acceleration when possible, otherwise will use highly optimized software blitting methods.\"", + "id" : 748, + "datetimeon" : "2007-07-22T04:31:03" + }, + { + "link" : "Surface.get_rect", + "content" : "hey thanks for the tip ive been searchin in vain for days", + "user_title" : "Anonymous", + "id" : 749, + "datetimeon" : "2007-07-22T10:05:15" + }, + { + "content" : "Yes, it's particularly an issue for when a sprite wants to move diagonally in a low resolution.\n\nWhen you're only moving sideways 1 pixel at a time, it's impossible to move diagonally without breaking conservation of momentum.", + "user_title" : "Anonymous", + "link" : "Rect.move_ip", + "datetimeon" : "2008-04-23T19:32:47", + "id" : 1782 + }, + { + "id" : 779, + "datetimeon" : "2007-07-28T12:28:50", + "link" : "pygame.event", + "content" : "# Mattew N. Brown copyright 2007\n# This is an example program for key input:\n\n ## IMPORT THEN EXECUTE IMPORTED MODULE ('*.py'): ##\nimport os, sys\nimport random\nimport pygame\nfrom pygame.locals import *\n ## UH!? WHAT IF IT ISN'T EXISTANT!?: ##\nif not pygame.font: print 'Warning, fonts disabled'\nif not pygame.mixer: print 'Warning, sound disabled'\n\n ## LOAD IMAGE AND SOUND: ##\ndef image_file_data(file_name, colorkey=None):\n try:\n image = pygame.image.load(file_name)\n except pygame.error, message:\n print 'ERROR: Image did not load:', file_name\n raise SystemExit, message\n image = image.convert()\n if colorkey is not None:\n if colorkey is -1:\n colorkey = image.get_at((0,0))\n image.set_colorkey(colorkey, RLEACCEL)\n return image, image.get_rect()\ndef sound_file_data(file_name):\n class NoneSound:\n def play(self): pass\n if not pygame.mixer:\n return NoneSound()\n try:\n sound = pygame.mixer.Sound(file_name)\n except pygame.error, message:\n print 'ERROR: Sound did not load:', file_name\n raise SystemExit, message\n return sound\ndef HEHEHE_font(size):\n fonti = pygame.font.Font(None, size)\n fonti.set_bold(0)\n return fonti\n ## IMAGE STRETCH AND ROTATE: ##\ndef HEHEHE_stretch_image (IMAGEY, wol, zol):\n #return pygame.transform.scale(IMAGEY, (wol, zol))\n return pygame.transform.scale(IMAGEY, (wol + IMAGEY.get_width(), zol + IMAGEY.get_height()))\ndef HEHEHE_rotate_image (IMAGEY, angle):\n center = (0, 0)\n rotate = pygame.transform.rotate\n IMAGEY = rotate(IMAGEY, angle)\n recty = IMAGEY.get_rect(center=center)\n return IMAGEY, recty\n ## DRAW IMAGE: ##\ndef draw_HEHEHE_image (IMAGEE, w, z):\n screen.blit(IMAGEE, (w, z))\ndef draw_HEHEHE_image_stretch (IMAGEE, w, z, wol, zol):\n IMAGEE = HEHEHE_stretch_image(IMAGEE, wol, zol)\n screen.blit(IMAGEE, (w, z))\ndef draw_HEHEHE_image_stretch_rotate (IMAGEE, w, z, wol, zol, angle):\n IMAGEE = HEHEHE_stretch_image(IMAGEE, wol, zol)\n IMAGEE, recty = HEHEHE_rotate_image(IMAGEE, angle)\n screen.blit(IMAGEE, (w + recty.x, z + recty.y))\n ## DRAW TEXT IMAGE: ##\ndef draw_HEHEHE_text (t, special, size, w, z, colory):\n fonty = HEHEHE_font(size)\n IMAGEE = fonty.render(t, special, colory)\n screen.blit(IMAGEE, (w, z))\ndef draw_HEHEHE_text_stretch (t, special, size, w, z, colory, wol, zol):\n fonty = HEHEHE_font(size)\n IMAGEE = fonty.render(t, special, colory)\n IMAGEE = HEHEHE_stretch_image(IMAGEE, wol, zol)\n screen.blit(IMAGEE, (w, z))\ndef draw_HEHEHE_text_stretch_rotate (t, special, size, w, z, colory, wol, zol, angle):\n fonty = HEHEHE_font(size)\n IMAGE = fonty.render(t, special, colory)\n IMAGE = HEHEHE_stretch_image(IMAGE, wol, zol)\n IMAGE, recty = HEHEHE_rotate_image(IMAGE, angle)\n screen.blit(IMAGE, (w + recty.x, z + recty.y))\n ### AAAH! FREAKY!! ###\nclock = pygame.time.Clock()\nImage_directory = \"PNG/\"\nSound_directory = \"SOUND/\"\n ### WHAT IN THE WORLD IS THIS!!??: ###\npygame.init()\nAPPLICATION_w_size = 700\nAPPLICATION_z_size = 500\nscreen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), RESIZABLE)\n#pygame.display.set_icon(image_file_data(Image_directory + \"ICON.PNG\", 0)[0])\npygame.display.set_caption('Mattew N. Brown copyright 2007')\npygame.mouse.set_visible(1)\n ### WHAT IN THE WORLD IS THIS!!!!!!!!!!!!??: ###\nbackground = pygame.Surface(screen.get_size())\nbackground = background.convert()\nbackground.fill((0, 0, 0))\n ### THIS IS DRIVING MY CAR CRAZYs!!!\nscreen.blit(background, (0, 0))\npygame.display.flip()\nrandom.seed()\n\n ## LOAD ALL IMAGES AND SOUNDS: ##\nimage_MAX = 0\nimage_file_name = [];\nimage = []\nimage_rect = []\nwhile (image_MAX <= (-1) ):\n I = image_MAX\n Itemp1, Itemp2 = image_file_data(Image_directory + image_file_name[I], 0)\n image += [Itemp1]\n image_rect += [Itemp2]\n image_MAX += 1\nsound_MAX = 0\nsound_file_name = [];\nsound = []\nsound_rect = []\nwhile (sound_MAX <= (-1) ):\n I = sound_MAX\n Itemp1, Itemp2 = sound_file_data(Sound_directory + sound_file_name[I])\n sound += [Itemp1]\n sound_rect += [Itemp2]\n sound_MAX += 1\n ## LOAD ONE AND ONLY MUSIC FILE: ##\n#pygame.mixer.music.load(d + 'PCDV0043.WAV')\n\n # QUIT\t none\n # ACTIVEEVENT gain, state\n # KEYDOWN\t unicode, key, mod\n # KEYUP\t key, mod\n # MOUSEMOTION pos, rel, buttons\n # MOUSEBUTTONUP pos, button\n # MOUSEBUTTONDOWN pos, button\n # JOYAXISMOTION joy, axis, value\n # JOYBALLMOTION joy, ball, rel\n # JOYHATMOTION joy, hat, value\n # JOYBUTTONUP joy, button\n # JOYBUTTONDOWN joy, button\n # VIDEORESIZE size, w, h\n # VIDEOEXPOSE none\n # USEREVENT code\n\n\n\n ## MAIN: ##\nif __name__ == '__main__':\n EE = ['', '', '', '', '', '', '', '', '', '',\n '', '', '', '', '']\n b = (190, 130, 110)\n COLORY = [b, b, b, b, b, b, b, b, b, b,\n b, b, b, b, b]\n angy = 0\n loopy = 1\n while (loopy == 1):\n angy += 1\n if angy > 360:\n angy = 1\n clock.tick(70)\n screen.blit(background, (0, 0))\n for e in pygame.event.get():\n if e.type == QUIT:\n loopy = 0\n #elif e.type == KEYDOWN and e.key == K_ESCAPE:\n # loopy = 0\n else:\n nnnnnn = -1\n if e.type == QUIT: nnnnnn = 0\n if e.type == ACTIVEEVENT: nnnnnn = 1\n if e.type == KEYDOWN: nnnnnn = 2\n if e.type == KEYUP: nnnnnn = 3\n if e.type == MOUSEMOTION: nnnnnn = 4\n if e.type == MOUSEBUTTONUP: nnnnnn = 5\n if e.type == MOUSEBUTTONDOWN: nnnnnn = 6\n if e.type == JOYAXISMOTION: nnnnnn = 7\n if e.type == JOYBALLMOTION: nnnnnn = 8\n if e.type == JOYHATMOTION: nnnnnn = 9\n if e.type == JOYBUTTONUP: nnnnnn = 10\n if e.type == JOYBUTTONDOWN: nnnnnn = 11\n if e.type == VIDEORESIZE:\n nnnnnn = 12\n APPLICATION_w_size = e.size[0]\n APPLICATION_z_size = e.size[1]\n screen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), RESIZABLE)\n if e.type == VIDEOEXPOSE: nnnnnn = 13\n if e.type == USEREVENT: nnnnnn = 14\n if nnnnnn != -1:\n EE[nnnnnn] = str(e); COLORY[nnnnnn] = (190, 200, 255)\n WOW = 0\n while (WOW < 14):\n draw_HEHEHE_text(\"E\" + str(WOW) + \" = \" + EE[WOW], 1, 24, 30, 20 + (WOW * 22), COLORY[WOW])\n if COLORY[WOW] == b:\n COLORY[WOW] = COLORY[WOW]\n else:\n COLORY[WOW] = (200, 240, 200)\n WOW += 1\n # QUIT\t none\n # ACTIVEEVENT gain, state\n # KEYDOWN\t unicode, key, mod\n # KEYUP\t key, mod\n # MOUSEMOTION pos, rel, buttons\n # MOUSEBUTTONUP pos, button\n # MOUSEBUTTONDOWN pos, button\n # JOYAXISMOTION joy, axis, value\n # JOYBALLMOTION joy, ball, rel\n # JOYHATMOTION joy, hat, value\n # JOYBUTTONUP joy, button\n # JOYBUTTONDOWN joy, button\n # VIDEORESIZE size, w, h\n # VIDEOEXPOSE none\n # USEREVENT code\n #I = 0\n #www = 0\n #while (www < 2):\n # www += 1\n # zzz = 0\n # while (zzz < 10):\n # zzz += 1\n # #if (I < image_MAX): draw_HEHEHE_image(I, www * 40, zzz * 40)\n # if (I < image_MAX): draw_HEHEHE_image_stretch_rotate(I, www * 40, zzz * 40, 40, 40, angy)\n # I += 1\n #draw_HEHEHE_text_stretch(\"BOOM!\", 1, 40, 330, 400, (255, 255, 255), 20, 20)\n #draw_HEHEHE_text_stretch_rotate(\"BOOM!\", 1, 40, 140, 400, (255, 255, 255), 0, 0, angy)\n #draw_HEHEHE_text_stretch_rotate(\"WEEEE!\", 1, 30, 450, 470, (255, 255, 255), 0, 0, angy)\n pygame.display.flip()", + "user_title" : "Anonymous" + }, + { + "link" : "pygame.draw", + "content" : "# Matthew N. Brown copyright 2007\n# Here is an example program that\n# draws: polygons, circles, and rectangles:\n#\n# You can copy this program on to\n# your own computer and run it.\n#\n\nimport os, sys\nimport random\nimport pygame\nfrom pygame.locals import *\nif not pygame.font: print 'Warning, fonts disabled'\nif not pygame.mixer: print 'Warning, sound disabled'\nimport time\nimport gc\nimport math\n\npygame.init()\nAPPLICATION_w_size = 700\nAPPLICATION_z_size = 500\nscreen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), RESIZABLE)\n#screen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), FULLSCREEN)\npygame.display.set_caption(\"HEHE test draw thingie program Matthew N. Brown copyright 2007\")\n#pygame.mouse.set_visible(0)\nglobal background\nbackground = pygame.Surface(screen.get_size())\nbackground.fill((0, 0, 0))\nscreen.blit(background, (0, 0))\npygame.display.flip()\nrandom.seed()\n\nplayer_w = 3\nplayer_z = 2\n\nx = -1\nmap_w_size = 10\nmap_z_size = 10\nmap = [[x, x, x, x, x, x, x, x, x, x, x],\n [x, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1],\n [x, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1],\n [x, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1],\n [x, 1, 1, 0, 0, 1, 0, 0, 4, 0, 0],\n [x, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0],\n [x, 1, 1, 0, 0, 2, 0, 1, 1, 0, 1],\n [x, 1, 1, 1, 0, 2, 0, 1, 0, 0, 1],\n [x, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1],\n [x, 1, 0, 4, 0, 1, 0, 0, 0, 0, 1],\n [x, 1, 0, 2, 2, 2, 2, 2, 2, 2, 1]]\n\nmap[player_z][player_w] = 3\n\n ## IMAGE STRETCH AND ROTATE: ##\ndef chilly_font(size):\n fonti = pygame.font.Font(None, size)\n fonti.set_bold(0)\n return fonti\n ## DRAW TEXT IMAGE: ##\ndef draw_chilly_text (t, special, size, w, z, colory):\n fonty = chilly_font(size)\n IMAGEE = fonty.render(t, special, colory)\n screen.blit(IMAGEE, (w, z))\n\n\n ### some functions: ###\ndef in_repeating_boundy (n, b1, b2):\n if n < b1: n = b2\n if n > b2: n = b1\n return n\ndef in_boundy (n, b1, b2):\n if n < b1: n = b1\n if n > b2: n = b2\n return n\ndef in_boundy2D ((w, z), (w1, z1, w2, z2)):\n if w < w1: w = w1\n if w > w2: w = w2\n if z < z1: z = z1\n if z > z2: z = z2\n return w, z\ndef chilly_distance (w1, z1, w2, z2):\n return math.sqrt(math.pow(w1 - w2, 2) + math.pow(z1 - z2, 2))\ndef chilly_rect_touching_rect(w1, z1, wol1, zol1, w2, z2, wol2, zol2):\n w2 -= w1\n z2 -= z1\n ww1 = -wol2\n zz1 = -zol2\n return (w2 >= ww1 and w2 <= wol1 and z2 >= zz1 and z2 <= zol1)\n\n ## keys and mouse stuff: ##\nglobal ky_held, ky_first_held, ky_time_last_pressed\nglobal mouse_w, mouse_z, mouse_inn, mouse_left_pressed, mouse_right_pressed, mouse_left_held, mouse_right_held\nnot_mouse_left_or_right_held = 1\nmouse_left_held = 0\nmouse_right_held = 0\nmouse_w = 0\nmouse_z = 0\nmouse_inn = 0\nky_held = [0]\nky_first_held = [0]\nky_time_last_pressed = [0]\nm = -1\nwhile (m < 500):\n m += 1\n ky_held += [0]\n ky_first_held += [0]\n ky_time_last_pressed += [0]\n\n ## MOUSE AND KEY FUNCTIONS: ##\ndef clear_kys():\n m = -1\n while (m < 500):\n m += 1\n ky_held[m] = 0\n ky_first_held[m] = 0\n ky_time_last_pressed[m] = 0\ndef mouse_left_pressed_CEV():\n global mouse_left_pressed\n if mouse_left_pressed: mouse_left_pressed = 0; return 1\ndef mouse_right_pressed_CEV():\n global mouse_right_pressed\n if mouse_right_pressed: mouse_right_pressed = 0; return 1\ndef old_style_ky(n):\n return (ky_first_held_CEV(n) or (ky_held[n] and ky_time_last_pressed[n] < time.time() - .3))\ndef ky_first_held_CEV(n):\n if (ky_first_held[n]):\n ky_first_held[n] = 0\n return 1\n else:\n return 0\ndef mouse_in_rect (w, z, wol, zol):\n return (mouse_w >= w and mouse_z >= z and mouse_w <= w + wol and mouse_z <= z + zol)\ndef mouse_in_circle (w, z, rad):\n dia = rad * 2\n if mouse_in_rect(w - rad, z - rad, w + dia, z + dia):\n return (chilly_distance(mouse_w, mouse_z, w, z) < rad)\n else:\n return 0\n\n ## CHECK FOR: KEYBOARD, MOUSE, JOYSTICK, AND OTHERY INPUTY: ##\ndef check_for_keys():\n global mouse_w, mouse_z, mouse_inn, mouse_left_pressed, mouse_right_pressed, mouse_left_held, mouse_right_held\n global loopy, letter_hitty\n global not_mouse_left_or_right_held\n for e in pygame.event.get():\n if e.type == QUIT:\n loopy = 0\n if e.type == ACTIVEEVENT:\n mouse_inn = (e.gain and (e.state == 1 or e.state == 6))\n if not mouse_inn:\n mouse_w = 0\n mouse_z = 0\n if e.type == KEYDOWN:\n ky_held[e.key] = 1\n ky_first_held[e.key] = 1\n ky_time_last_pressed[e.key] = time.time()\n if (e.key >= 97 and e.key <= 122):\n letter_hitty = e.unicode.lower()\n if e.type == KEYUP:\n ky_held[e.key] = 0\n #ky_first_held[e.key] = 0\n if e.type == MOUSEMOTION:\n mouse_w = e.pos[0]\n mouse_z = e.pos[1]\n if e.type == MOUSEBUTTONUP:\n if e.button == 1: mouse_left_held = 0\n if e.button == 3: mouse_right_held = 0\n if not mouse_left_held and not mouse_right_held: not_mouse_left_or_right_held = 1\n if e.type == MOUSEBUTTONDOWN:\n mouse_left_pressed = e.button == 1\n mouse_right_pressed = e.button == 3\n mouse_left_held = mouse_left_held or e.button == 1\n mouse_right_held = mouse_right_held or e.button == 3\n if mouse_left_held or mouse_right_held: not_mouse_left_or_right_held = 0\n if e.type == JOYAXISMOTION: nnnnnn = 7\n if e.type == JOYBALLMOTION: nnnnnn = 8\n if e.type == JOYHATMOTION: nnnnnn = 9\n if e.type == JOYBUTTONUP: nnnnnn = 10\n if e.type == JOYBUTTONDOWN: nnnnnn = 11\n if e.type == VIDEORESIZE:\n global background, Dimage_editing_screen, screen, APPLICATION_w_size, APPLICATION_z_size\n APPLICATION_w_size = e.size[0]\n APPLICATION_z_size = e.size[1]\n screen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), RESIZABLE)\n background = pygame.Surface((APPLICATION_w_size, APPLICATION_z_size))\n if e.type == VIDEOEXPOSE: nnnnnn = 13\n if e.type == USEREVENT: nnnnnn = 14\n\n ### MORE STUFF: ###\nHE_HE_surfacey = pygame.Surface((40, 40))\ncolor1 = (200, 200, 200)\ncolor2 = (200, 0, 0)\ncolor3 = (0, 200, 0)\ncolor4 = (130, 180, 180)\nblack_colory = (0, 0, 0)\nHE_HE_surfacey.fill(black_colory)\n\ndef try_to_push_block(w, z, wo, zo):\n if map[z][w] == 1:\n w_pushed = w + wo\n z_pushed = z + zo\n w_pushed, z_pushed = in_boundy2D((w_pushed, z_pushed), (0, 0, map_w_size, map_z_size))\n if map[z_pushed][w_pushed] == 0:\n map[z][w] = 0\n map[z_pushed][w_pushed] = 1\n\ndef draw_map():\n ww = 0\n while ww < map_w_size:\n ww += 1\n zz = 0\n while zz < map_z_size:\n zz += 1\n n = map[zz][ww]\n screen.blit(HE_HE_surfacey, (ww * 40, zz * 40))\n if n == 1:\n pygame.draw.rect(screen, color1, (ww * 40, zz * 40, 40, 40), 2)\n elif n == 2:\n #pygame.draw.rect(screen, color2, (ww * 40, zz * 40, 40, 40), 2)\n pygame.draw.circle(screen, color2, (ww * 40 + 20, zz * 40 + 20), 17, 2)\n elif n == 3:\n #pygame.draw.rect(screen, color3, (ww * 40, zz * 40, 40, 40), 2)\n locy_w = ww * 40\n locy_z = zz * 40\n point1 = (20 + locy_w, 10 + locy_z)\n point2 = (40 + locy_w, 12 + locy_z)\n point3 = (30 + locy_w, 19 + locy_z)\n point4 = (30 + locy_w, 30 + locy_z)\n point5 = (20 + locy_w, 20 + locy_z)\n points = (point1, point2, point3, point4, point5)\n pygame.draw.polygon(screen, color3, points, 2)\n elif n == 4:\n pygame.draw.rect(screen, color4, (ww * 40, zz * 40, 40, 40), 4)\n\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n\n# NOTE: w = x\n# NOTE: z = y\n# -- HE, HE, Bad habit of mine . . .\n\n ## MAIN: ##\nif __name__ == '__main__':\n\n # THE MAIN, MAIN, MAIN LOOP:\n loopy = 1\n while (loopy == 1):\n\n\n mouse_left_pressed = 0\n mouse_right_pressed = 0\n check_for_keys()\n\n draw_map()\n draw_chilly_text('Press the arrow keys to move . . .', 0, 20, 0, 0, (255, 255, 255))\n\n wa = player_w\n za = player_z\n map[player_z][player_w] = 0\n if old_style_ky(276): player_w -= 1\n if old_style_ky(273): player_z -= 1\n if old_style_ky(275): player_w += 1\n if old_style_ky(274): player_z += 1\n player_w, player_z = in_boundy2D((player_w, player_z), (0, 0, map_w_size, map_z_size))\n try_to_push_block(player_w, player_z, player_w - wa, player_z - za)\n if map[player_z][player_w] != 0:\n player_w = wa\n player_z = za\n map[player_z][player_w] = 3\n\n #if ky_first_held[27]: loopy = 0\n pygame.display.flip()", + "user_title" : "Anonymous", + "id" : 781, + "datetimeon" : "2007-07-29T17:05:04" + }, + { + "link" : "pygame.draw.circle", + "user_title" : "Anonymous", + "content" : "# Matthew N. Brown copyright 2007\n# Here is an example program that\n# draws a bouncing ball using: pygame.draw.circle\n#\n# You can copy this program on to\n# your own computer and run it.\n#\n\nimport os, sys\nimport random\nimport pygame\nfrom pygame.locals import *\nif not pygame.font: print 'Warning, fonts disabled'\nif not pygame.mixer: print 'Warning, sound disabled'\nimport time\nimport gc\nimport math\n\npygame.init()\nAPPLICATION_w_size = 700\nAPPLICATION_z_size = 500\nscreen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), RESIZABLE)\n#screen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), FULLSCREEN)\npygame.display.set_caption(\"HEHE test circle thingie program Matthew N. Brown copyright 2007\")\n#pygame.mouse.set_visible(0)\nglobal background\nbackground = pygame.Surface(screen.get_size())\nbackground.fill((0, 0, 0))\nscreen.blit(background, (0, 0))\npygame.display.flip()\nrandom.seed()\n\n\n ## IMAGE STRETCH AND ROTATE: ##\ndef HEHEHE_font(size):\n fonti = pygame.font.Font(None, size)\n fonti.set_bold(0)\n return fonti\n ## DRAW TEXT IMAGE: ##\ndef draw_HEHEHE_text (t, special, size, w, z, colory):\n fonty = HEHEHE_font(size)\n IMAGEE = fonty.render(t, special, colory)\n screen.blit(IMAGEE, (w, z))\n\n\n ### some functions: ###\ndef in_repeating_boundy (n, b1, b2):\n if n < b1: n = b2\n if n > b2: n = b1\n return n\ndef in_boundy (n, b1, b2):\n if n < b1: n = b1\n if n > b2: n = b2\n return n\ndef in_boundy2D ((w, z), (w1, z1, w2, z2)):\n if w < w1: w = w1\n if w > w2: w = w2\n if z < z1: z = z1\n if z > z2: z = z2\n return w, z\ndef HEHEHE_distance (w1, z1, w2, z2):\n return math.sqrt(math.pow(w1 - w2, 2) + math.pow(z1 - z2, 2))\ndef HEHEHE_rect_touching_rect(w1, z1, wol1, zol1, w2, z2, wol2, zol2):\n w2 -= w1\n z2 -= z1\n ww1 = -wol2\n zz1 = -zol2\n return (w2 >= ww1 and w2 <= wol1 and z2 >= zz1 and z2 <= zol1)\n\n ## keys and mouse stuff: ##\nglobal ky_held, ky_first_held, ky_time_last_pressed\nglobal mouse_w, mouse_z, mouse_inn, mouse_left_pressed, mouse_right_pressed, mouse_left_held, mouse_right_held\nnot_mouse_left_or_right_held = 1\nmouse_left_held = 0\nmouse_right_held = 0\nmouse_w = 0\nmouse_z = 0\nmouse_inn = 0\nky_held = [0]\nky_first_held = [0]\nky_time_last_pressed = [0]\nm = -1\nwhile (m < 500):\n m += 1\n ky_held += [0]\n ky_first_held += [0]\n ky_time_last_pressed += [0]\n\n ## MOUSE AND KEY FUNCTIONS: ##\ndef clear_kys():\n m = -1\n while (m < 500):\n m += 1\n ky_held[m] = 0\n ky_first_held[m] = 0\n ky_time_last_pressed[m] = 0\ndef mouse_left_pressed_CEV():\n global mouse_left_pressed\n if mouse_left_pressed: mouse_left_pressed = 0; return 1\ndef mouse_right_pressed_CEV():\n global mouse_right_pressed\n if mouse_right_pressed: mouse_right_pressed = 0; return 1\ndef old_style_ky(n):\n return (ky_first_held_CEV(n) or (ky_held[n] and ky_time_last_pressed[n] < time.time() - .3))\ndef ky_first_held_CEV(n):\n if (ky_first_held[n]):\n ky_first_held[n] = 0\n return 1\n else:\n return 0\ndef mouse_in_rect (w, z, wol, zol):\n return (mouse_w >= w and mouse_z >= z and mouse_w <= w + wol and mouse_z <= z + zol)\ndef mouse_in_circle (w, z, rad):\n dia = rad * 2\n if mouse_in_rect(w - rad, z - rad, w + dia, z + dia):\n return (HEHEHE_distance(mouse_w, mouse_z, w, z) < rad)\n else:\n return 0\n\n ## CHECK FOR: KEYBOARD, MOUSE, JOYSTICK, AND OTHERY INPUTY: ##\ndef check_for_keys():\n global mouse_w, mouse_z, mouse_inn, mouse_left_pressed, mouse_right_pressed, mouse_left_held, mouse_right_held\n global loopy, letter_hitty\n global not_mouse_left_or_right_held\n for e in pygame.event.get():\n if e.type == QUIT:\n loopy = 0\n if e.type == ACTIVEEVENT:\n mouse_inn = (e.gain and (e.state == 1 or e.state == 6))\n if not mouse_inn:\n mouse_w = 0\n mouse_z = 0\n if e.type == KEYDOWN:\n ky_held[e.key] = 1\n ky_first_held[e.key] = 1\n ky_time_last_pressed[e.key] = time.time()\n if (e.key >= 97 and e.key <= 122):\n letter_hitty = e.unicode.lower()\n if e.type == KEYUP:\n ky_held[e.key] = 0\n #ky_first_held[e.key] = 0\n if e.type == MOUSEMOTION:\n mouse_w = e.pos[0]\n mouse_z = e.pos[1]\n if e.type == MOUSEBUTTONUP:\n if e.button == 1: mouse_left_held = 0\n if e.button == 3: mouse_right_held = 0\n if not mouse_left_held and not mouse_right_held: not_mouse_left_or_right_held = 1\n if e.type == MOUSEBUTTONDOWN:\n mouse_left_pressed = e.button == 1\n mouse_right_pressed = e.button == 3\n mouse_left_held = mouse_left_held or e.button == 1\n mouse_right_held = mouse_right_held or e.button == 3\n if mouse_left_held or mouse_right_held: not_mouse_left_or_right_held = 0\n if e.type == JOYAXISMOTION: nnnnnn = 7\n if e.type == JOYBALLMOTION: nnnnnn = 8\n if e.type == JOYHATMOTION: nnnnnn = 9\n if e.type == JOYBUTTONUP: nnnnnn = 10\n if e.type == JOYBUTTONDOWN: nnnnnn = 11\n if e.type == VIDEORESIZE:\n global background, Dimage_editing_screen, screen, APPLICATION_w_size, APPLICATION_z_size\n APPLICATION_w_size = e.size[0]\n APPLICATION_z_size = e.size[1]\n screen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), RESIZABLE)\n background = pygame.Surface((APPLICATION_w_size, APPLICATION_z_size))\n if e.type == VIDEOEXPOSE: nnnnnn = 13\n if e.type == USEREVENT: nnnnnn = 14\n\n ### MORE STUFF: ###\nball_w = 30.0\nball_z = 20.0\n\nball_wol = 4.0\nball_zol = -1.0\n\ngravity_w = 0.0\ngravity_z = 1.0\n\nradius = 11.0\n\nmakes_ball_slower_per_bounce = 1.2\n\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n\n# NOTE: w = x\n# NOTE: z = y\n# -- HE, HE, Bad habit of mine . . .\n\n ## MAIN: ##\nif __name__ == '__main__':\n\n # THE MAIN, MAIN, MAIN LOOP:\n loopy = 1\n while (loopy == 1):\n\n t = time.time()\n while t > time.time() - .03:\n pass\n mouse_left_pressed = 0\n mouse_right_pressed = 0\n check_for_keys()\n\n ball_wol += gravity_w\n ball_zol += gravity_z\n\n if old_style_ky(276): ball_wol -= 12\n if old_style_ky(273): ball_zol -= 22\n if old_style_ky(275): ball_wol += 12\n if old_style_ky(274): ball_zol += 22\n if ky_held[115]: ball_wol = 0; ball_zol = 0\n if ky_held[99]: ball_wol = (random.random() * 400) - 200; ball_zol = (random.random() * 400) - 200\n\n ball_w += ball_wol\n ball_z += ball_zol\n\n if ball_w < radius: ball_w = radius; ball_wol = -(ball_wol / makes_ball_slower_per_bounce)\n if ball_z < radius: ball_z = radius; ball_zol = -(ball_zol / makes_ball_slower_per_bounce)\n if ball_w > APPLICATION_w_size - radius: ball_w = APPLICATION_w_size - radius; ball_wol = -(ball_wol / makes_ball_slower_per_bounce)\n if ball_z > APPLICATION_z_size - radius: ball_z = APPLICATION_z_size - radius; ball_zol = -(ball_zol / makes_ball_slower_per_bounce)\n\n screen.fill((0, 0, 0))\n draw_HEHEHE_text('Press the arrow keys to move ball.', 0, 25, 0, 0, (255, 255, 255))\n draw_HEHEHE_text('Hold S to stop ball.', 0, 25, 0, 30, (255, 255, 255))\n draw_HEHEHE_text('press C to make ball go crazy.', 0, 25, 0, 70, (255, 255, 255))\n pygame.draw.circle(screen, (200, 200, 200), (int(ball_w), int(ball_z)), int(radius))\n\n #if ky_first_held[27]: loopy = 0\n pygame.display.flip()", + "id" : 782, + "datetimeon" : "2007-07-29T19:02:50" + }, + { + "link" : "You have a great site. All in your web is very useful. Please keep on working.", + "content" : "", + "user_title" : "Anonymous", + "id" : 796, + "datetimeon" : "2007-08-08T10:35:27" + }, + { + "content" : "Is this fast ? What is better for software systems ?", + "user_title" : "Anonymous", + "link" : "Surface.blit", + "datetimeon" : "2007-08-12T18:03:51", + "id" : 800 + }, + { + "link" : "pygame.display.set_icon", + "content" : "I have not tried photoshop or something to create alpha channels in bitmaps directly. Instead, I use a mask color in a 'normal' bitmap, and make that look transparant in the icon.\ncreate a bitmap in mspaint, black areas will be transparant, size 32x32 pixels, save it as 'icon.bmp'.\n \nthen create a file named icon.py and put this in it:\n###\nimport pygame\n\ndef seticon(iconname):\n \"\"\"\n give an iconname, a bitmap sized 32x32 pixels, black (0,0,0) will be alpha channel\n \n the windowicon will be set to the bitmap, but the black pixels will be full alpha channel\n \n can only be called once after pygame.init() and before somewindow = pygame.display.set_mode()\n \"\"\"\n icon=pygame.Surface((32,32))\n icon.set_colorkey((0,0,0))#and call that color transparant\n rawicon=pygame.image.load(iconname)#must be 32x32, black is transparant\n for i in range(0,32):\n for j in range(0,32):\n icon.set_at((i,j), rawicon.get_at((i,j)))\n pygame.display.set_icon(icon)#set wind\n\npygame.init()\nseticon('icon.bmp')\nwindow=pygame.display.set_mode((250,250))\nbackground=pygame.Surface(window.get_size())\nbackground.fill((50,50,50))\n \nwhile 1:\n for event in pygame.event.get():\n if not event.type == pygame.MOUSEMOTION:#print all events, but not the mousemoves :) for feedback info\n print str(event)\n if event.type == pygame.QUIT: # close window cross (upper right corner) pressed: exit\n raise SystemExit\n elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: #escape pressed: exit\n raise SystemExit\n window.blit(background, (0, 0))#fresh background \n #insert other blitty things here onto the window \n pygame.display.flip()#show completed window\n###\nto see the effect: a transparant icon, based on a normal bitmap with a mask color. \nYou can easily modify the code to use an different mask color or filename if needed.", + "user_title" : "Anonymous", + "id" : 803, + "datetimeon" : "2007-08-12T20:35:22" + }, + { + "datetimeon" : "2007-08-13T03:15:52", + "id" : 805, + "content" : "This function's resume is wrong, it says:\nGroup.has(*sprites): return None\nshould say\nGroup.has(*sprites): return Boolean", + "user_title" : "Anonymous", + "link" : "Group.has" + }, + { + "datetimeon" : "2007-08-17T03:35:39", + "id" : 809, + "content" : "In other words, this returns the area in which 2 Rects overlap.\nThis implies that rectA.clip(rectB) == rectB.clip(rectA).", + "user_title" : "Anonymous", + "link" : "Rect.clip" + }, + { + "user_title" : "Anonymous", + "content" : "There should be more types of sounds loadable. Also a way to save them.", + "link" : "pygame.mixer.Sound", + "datetimeon" : "2007-08-25T14:44:46", + "id" : 815 + }, + { + "user_title" : "Anonymous", + "content" : "if you want to use the same function but from module PIL\n\n from PIL import Image\n im = pygame.image.load (\"image.png\")\n s = pygame.image.tostring (im, \"RGBX\")\n temp = Image.fromstring (\"RGBX\", im.get_size (), s)\n tu = (0,0, im.get_size () [0]-1, im.get_size () [1] - 1)\n temp = temp.transform (size2, Image.EXTENT, tu, Image.BICUBIC)\n mode = temp.mode\n size = temp.size\n data = temp.tostring()\n res = pygame.image.fromstring (data, size, mode)", + "link" : "pygame.transform.rotate", + "datetimeon" : "2007-08-26T09:36:23", + "id" : 817 + }, + { + "datetimeon" : "2007-08-26T09:37:15", + "id" : 818, + "user_title" : "Anonymous", + "content" : "if you want to use the same function but from module PIL\n\n from PIL import Image\n im = pygame.image.load (\"image.png\")\n s = pygame.image.tostring (im, \"RGBX\")\n temp = Image.fromstring (\"RGBX\", im.get_size (), s)\n tu = (0,0, im.get_size () [0]-1, im.get_size () [1] - 1)\n temp = temp.transform (size2, Image.EXTENT, tu, Image.BICUBIC)\n mode = temp.mode\n size = temp.size\n data = temp.tostring()\n res = pygame.image.fromstring (data, size, mode)", + "link" : "pygame.transform.scale" + }, + { + "link" : "Sound.set_volume", + "user_title" : "Anonymous", + "content" : "Numbers greater than 1.0 seem to be interpreted as 1.0.\nNegative numbers are made positive (absolute value)\n\n-4.0 = 4.0 = 1.0", + "id" : 820, + "datetimeon" : "2007-08-26T21:15:03" + }, + { + "user_title" : "Anonymous", + "content" : "If you try to use the alpha in [Color] its not applied, \nbut Draw.lines applies alpha in [Color]", + "link" : "pygame.draw.aalines", + "datetimeon" : "2007-08-27T12:19:45", + "id" : 821 + }, + { + "id" : 823, + "datetimeon" : "2007-08-31T04:29:56", + "link" : "pygame.key.set_repeat", + "content" : "Is it possible to use this on only certain keys,\nor to use different values for different groups of keys?\n\nFor example, say you wanted to have a certain value assigned to the player movement keys,\nbut a different value assigned to the attack keys, and no value set for the menu keys.", + "user_title" : "Anonymous" + }, + { + "id" : 825, + "datetimeon" : "2007-09-04T04:16:59", + "link" : "pygame.joystick.Joystick", + "content" : "That depends on a number of things. For digital D-pads (like on a SNES controller)\nthe values reported will always be \"full blast\" because that's how the gamepad\nhardware reports the direction of the D-pad. For analog sticks, like the ones in\nthe middle of a PS2 controller, they will usually report a value in the range of\na 32-bit integer (or maybe a 16-bit integer, or even a float--I don't really know).\nIt all depends on the hardware, not to mention the drivers of your OS.", + "user_title" : "Anonymous" + }, + { + "link" : "pygame.draw.rect", + "user_title" : "Anonymous", + "content" : "This will make one single pixel a Color at coordanates x, y on a Surface:\npygame.draw.rect(Surface, Color, (x, y, 1, 1))", + "id" : 826, + "datetimeon" : "2007-09-05T21:50:41" + }, + { + "id" : 1821, + "datetimeon" : "2008-05-04T00:13:13", + "link" : "Rect.collidepoint", + "user_title" : "Anonymous", + "content" : "Hi Tim,\n\nMaybe you realised your error by now, but here is a little clarification for the\npeople reading your comment.\n\nYou created a square with a side of 4 pixel at the position (0, 0). \n\n 0 1 2 3 4\n0 x x x x .\n1 x x x x .\n2 x x x x .\n3 x x x x .\n4 . . . . .\n\nSo of course any position with x or y >= 4 will be outside the square." + }, + { + "link" : "pygame.draw.circle", + "content" : "How to draw a single pixel: draw a circle with radius zero! Took me a while to find this.\n\ncircle( ..., 0 ) will give you a single pixel", + "user_title" : "Anonymous", + "id" : 837, + "datetimeon" : "2007-09-08T02:17:09" + }, + { + "id" : 847, + "datetimeon" : "2007-09-09T17:05:37", + "link" : "pygame.event.set_allowed", + "user_title" : "Anonymous", + "content" : "It seems to be true that None will cause set_allowed to BLOCK all events.\n...even though it was April 1st.\n\nset_blocked(None) doesn't seem to have any effect like described above." + }, + { + "content" : "set_blocked(None) doesn't seem to have this effect at all, see the comment in set_allowed after April 1st.", + "user_title" : "Anonymous", + "link" : "pygame.event.set_blocked", + "datetimeon" : "2007-09-09T17:06:19", + "id" : 848 + }, + { + "datetimeon" : "2007-09-12T21:29:31", + "id" : 851, + "user_title" : "Anonymous", + "content" : "Antialised text *does* work on black backgrounds, you just have to be careful only to\nblit it once, because the parts with less than full alpha will build up \n(very quickly if you are blitting over and over.)", + "link" : "Font.render" + }, + { + "link" : "Surface.set_at", + "content" : "There is an alternative to setting pixels one-at-a-time that is much, much faster. Pygame's Surfarray module will allow you to access the pixels like an array.\n\nIf you need to manipulate pixels on an individual level, it is strongly recommended that you use Surfarrays instead of set_at.", + "user_title" : "Anonymous", + "id" : 1842, + "datetimeon" : "2008-05-07T10:56:34" + }, + { + "link" : "pygame.mixer.music.fadeout", + "user_title" : "Anonymous", + "content" : "fade out does NOT block till it is finished in windows!!!!", + "id" : 1084, + "datetimeon" : "2007-11-12T00:14:47" + }, + { + "content" : "There is no explanation of 'color' argument...?", + "user_title" : "Anonymous", + "link" : "pygame.draw.rect", + "datetimeon" : "2007-10-09T13:57:03", + "id" : 913 + }, + { + "link" : "pygame.draw.rect", + "user_title" : "Anonymous", + "content" : "colors are usually done as a tuple\n(red light out of 255,green light out of 255, blue light out of 255).", + "id" : 915, + "datetimeon" : "2007-10-09T20:01:34" + }, + { + "id" : 932, + "datetimeon" : "2007-10-17T11:20:52", + "link" : "pygame.mixer.Sound", + "user_title" : "Anonymous", + "content" : "OGG is a container format... They probably meant only OGG/Vorbis." + }, + { + "id" : 933, + "datetimeon" : "2007-10-17T16:49:42", + "link" : "pygame.draw.rect", + "user_title" : "Anonymous", + "content" : "You can use Surface.set_at((x,y), colour) to set a pixel." + }, + { + "link" : "Font.render", + "content" : "It is a dissapointment to discover that \\n does not\nwork with the default font and merely shows a box.", + "user_title" : "Anonymous", + "id" : 937, + "datetimeon" : "2007-10-18T18:45:51" + }, + { + "content" : "# This is an example that uses pygame.draw.rect:\nimport os, sys\nimport random\nimport pygame\nfrom pygame.locals import *\npygame.init()\nAPPLICATION_x_size = 400\nAPPLICATION_y_size = 300\nscreen = pygame.display.set_mode((APPLICATION_x_size, APPLICATION_y_size))\npygame.display.set_caption('Fun Boring Example comes with Source Code too!!')\npygame.mouse.set_visible(True)\n#pygame.mouse.set_visible(False)\nblack_square_that_is_the_size_of_the_screen = pygame.Surface(screen.get_size())\nblack_square_that_is_the_size_of_the_screen.fill((0, 0, 0))\nscreen.blit(black_square_that_is_the_size_of_the_screen, (0, 0))\npygame.display.flip()\nWeeee = True\nwhile Weeee:\n # a color can be: (0 to 255, 0 to 255, 0 to 255)\n My_red_color = (255, 0, 0)\n My_blue_color = (0, 0, 255)\n My_green_color = (0, 255, 0)\n My_yellow_color = (255, 255, 0)\n WHITE_WHITE_HOORAY = (255, 255, 255)\n My_light_red_color = (255, 180, 180)\n My_light_blue_color = (190, 190, 255)\n # \"screen.set_at((x, y), Color)\" and \"pygame.draw.rect(screen, Color, (x, y, x_size, y_size))\" draw colors on to an \"in computer memory image\" called: \"screen\"\n screen.set_at(( 1, 1), My_yellow_color)\n screen.set_at(( 2, 2), My_yellow_color)\n screen.set_at(( 3, 3), My_yellow_color)\n screen.set_at(( 4, 4), My_yellow_color)\n screen.set_at(( 5, 5), My_yellow_color)\n screen.set_at(( 6, 6), My_yellow_color)\n screen.set_at(( 7, 7), My_yellow_color)\n screen.set_at(( 8, 8), My_yellow_color)\n screen.set_at(( 9, 9), My_yellow_color)\n screen.set_at((10, 10), My_yellow_color)\n screen.set_at((11, 11), My_yellow_color)\n screen.set_at((12, 12), My_yellow_color)\n screen.set_at((13, 13), My_yellow_color)\n screen.set_at((14, 14), My_yellow_color)\n screen.set_at((15, 15), My_yellow_color)\n screen.set_at((16, 16), My_yellow_color)\n screen.set_at((17, 17), My_yellow_color)\n screen.set_at((18, 18), My_yellow_color)\n screen.set_at((19, 19), My_yellow_color)\n screen.set_at((20, 20), My_yellow_color)\n pygame.draw.rect(screen, My_red_color, (50, 50, 10, 10))\n pygame.draw.rect(screen, My_red_color, (50, 120, 20, 20))\n pygame.draw.rect(screen, My_blue_color, (50, 150, 30, 30))\n pygame.draw.rect(screen, My_blue_color, (50, 1000, 1000, 10))\n pygame.draw.rect(screen, My_green_color, (200, 10, 40, 40))\n pygame.draw.rect(screen, My_light_red_color, (10, 200, 50, 50))\n pygame.draw.rect(screen, My_light_blue_color, (200, 200, 60, 60))\n pygame.draw.rect(screen, My_light_blue_color, (100, 200, 10, 2))\n pygame.draw.rect(screen, WHITE_WHITE_HOORAY, (0, 100, 50, 52))\n # If you delete the below line you should no longer see the vibrant colors.\n pygame.display.flip()\n # if the 'X' button is pressed the window should close:\n Geesh = pygame.event.get()\n if len(Geesh) > 0:\n if Geesh[0].type == QUIT: Weeee = False\n## Once this line is reached the window should close", + "user_title" : "Anonymous", + "link" : "pygame.draw.rect", + "datetimeon" : "2007-10-18T19:23:51", + "id" : 938 + }, + { + "user_title" : "Anonymous", + "content" : "If your program has sources of events that are not managed by pygame, such as\nnetwork socket data, or large files, you must either add a thread that selects\non the source and injects pygame events, or poll the source briefly and rapidly.", + "link" : "pygame.event", + "datetimeon" : "2007-10-23T23:20:11", + "id" : 955 + }, + { + "link" : "pygame.mixer.music.load", + "user_title" : "Anonymous", + "content" : "It never seems to be able to load this (error reported, cannot read). But when I run a script that directly runs it(without the loop), it works fine\n\n(songs is a list of filenames loaded form a .txt file)\n\ncurrent_song = 0\nwhile 1:\n if pygame.mixer.music.get_busy() == False:\n print songs[current_song]\n pygame.mixer.music.load(songs[current_song])\n pygame.mixer.music.play() \n current_song += 1", + "id" : 965, + "datetimeon" : "2007-10-25T21:19:18" + }, + { + "datetimeon" : "2007-11-01T18:47:48", + "id" : 1002, + "content" : "When you make an icon make a 16x16 icon and then scale it to 32x32 pixels.\nIf you make it 16x16 pixels it looks distorted.\n\nI usally have a transparent 32x32 .gif icon for my games.", + "user_title" : "Anonymous", + "link" : "pygame.display.set_icon" + }, + { + "datetimeon" : "2007-11-01T18:49:22", + "id" : 1003, + "user_title" : "Anonymous", + "content" : "When you make an icon make a 16x16 icon and then scale it to 32x32 pixels.\nIf you make it 16x16 pixels it looks distorted.\n\nI usally have a transparent 32x32 .gif icon for my games.", + "link" : "pygame.display.set_icon" + }, + { + "content" : "Always set the icon before you call pygame.display.set_mode", + "user_title" : "Anonymous", + "link" : "pygame.display.set_icon", + "datetimeon" : "2007-11-01T18:50:24", + "id" : 1004 + }, + { + "datetimeon" : "2007-11-01T18:56:17", + "id" : 1005, + "content" : "Here's a quick script for loading images:\n\ndef load_image(file, colorkey=False):\n file = os.path.join('data', file)\n try:\n image = pygame.image.load(file)\n colorkey = image.get_at((0, 0))\n if colorkey is True:\n image.set_colorkey(colorkey, pygame.RLEACCEL)\n except:\n print 'Unable to load: ' + file\n return image.convert_alpha() #Convert any transparency in the image", + "user_title" : "Anonymous", + "link" : "pygame.image.load" + }, + { + "id" : 1006, + "datetimeon" : "2007-11-01T19:00:33", + "link" : "Font.render", + "content" : "I agree that it is a dissapointment about \\n, but anti-aliasing works fine for me!", + "user_title" : "Anonymous" + }, + { + "datetimeon" : "2007-11-03T04:27:28", + "id" : 1013, + "user_title" : "Anonymous", + "content" : "this gives me 6 modules initialised OK, 0 failed.\nbut i only know of 5 modules that have to be inited:\ncdrom, display, font, joystick, mixer. which one did i miss?", + "link" : "pygame.init" + }, + { + "datetimeon" : "2007-11-05T05:06:25", + "id" : 1032, + "user_title" : "Anonymous", + "content" : "Yeah, it is fast, but what do you want to compare it to when you\nask \"what is better\"? Within PyGame, there's no alternative to\nusing Surface.blit. I'd suggest you either use that, or if you find\nit too slow (but really make sure it's too slow for you, i.e. test\nif the real problem might be using flip instead of update), use\nOpenGL.", + "link" : "Surface.blit" + }, + { + "user_title" : "Anonymous", + "content" : "Rects do not move to floating point numbers. Only integers.\n\n\nSo if you do:\n\nself.rect.move_ip(4.5, 0)\n\nit will actually execute:\n\nself.rect.move_ip(4, 0)\n\n\nThis limitation is really bad if you're making a small screen platformer.\nI hope that Rects will move to floating point numbers in pygame 1.8.", + "link" : "Rect.move_ip", + "datetimeon" : "2007-11-05T09:47:30", + "id" : 1034 + }, + { + "link" : "pygame.draw", + "user_title" : "Anonymous", + "content" : "Copy this to your computer and save it as a .py file to run a little trig demo.\n\n\n\n#! usr/bin/env python\n\nimport pygame, math\nfrom pygame.locals import *\n\nclass Ship:\n def __init__(self):\n self.image=pygame.Surface((40, 40))\n self.rect=self.image.get_rect(center=(320,240))\n self.x=200\n self.y=150\n self.x_vel=0\n self.y_vel=0\n self.angle=0\n self.point_list = [(0, -20), (2.25, -20), (3.0, -6), (4.05, -20)]\n def update(self):\n self.rect.centerx=self.x\n self.rect.centery=self.y\n self.x+=self.x_vel\n self.y+=self.y_vel\n key = pygame.key.get_pressed()\n if key[K_RIGHT]:\n self.angle -= 4\n if key[K_LEFT]:\n self.angle += 4\n if key[K_UP]:\n self.accel(0.1)\n if key[K_DOWN]:\n self.accel(-0.1)\n def draw(self, surface):\n surface.blit(self.image, self.rect)\n self.image.fill((0, 0, 0))\n\tpoint_list = []\n\tself.angle2 = math.radians(self.angle)\n\tfor p in self.point_list:\n radian, radius = p\n x = int(math.sin(radian+self.angle2)*radius)\n y = int(math.cos(radian+self.angle2)*radius)\n\t point_list.append((x+self.image.get_width()/2,y+self.image.get_height()/2))\n\tpygame.draw.polygon(self.image, (255,255,255), point_list, 1)\n def accel(self, accel_speed):\n self.x_vel += math.sin(self.angle*2*math.pi/360)*-accel_speed\n self.y_vel += math.cos(self.angle*2*math.pi/360)*-accel_speed\n def wrap(self, surface):\n if self.x >= surface.get_width() + self.image.get_width()/2:\n self.x = -self.image.get_width()/2\n if self.x <= -self.image.get_width()/2 - 1:\n self.x = surface.get_width() + self.image.get_width()/2\n if self.y >= surface.get_height() + self.image.get_height()/2:\n self.y = -self.image.get_height()/2\n if self.y <= -self.image.get_height()/2 - 1:\n self.y = surface.get_height() + self.image.get_height()/2\n\ndef main():\n pygame.init()\n pygame.display.set_caption('trig demo.py')\n screen = pygame.display.set_mode((400, 300))\n ship = Ship()\n clock = pygame.time.Clock()\n\n while 1:\n clock.tick(60)\n event = pygame.event.poll()\n if event.type == QUIT:\n return\n if event.type == KEYDOWN:\n if event.key == K_ESCAPE:\n return\n\n screen.fill((0, 0, 0))\n ship.draw(screen)\n ship.update()\n ship.wrap(screen)\n pygame.display.flip()\n\n\nif __name__ == '__main__':\n main()", + "id" : 1050, + "datetimeon" : "2007-11-07T17:25:07" + }, + { + "user_title" : "Anonymous", + "content" : "Here is a neat little trig demo:\n\n\n\n#! usr/bin/env python\n\nimport pygame, math\nfrom pygame.locals import *\n\nclass Ship:\n def __init__(self):\n self.image=pygame.Surface((40, 40))\n self.rect=self.image.get_rect(center=(320,240))\n self.x=200\n self.y=150\n self.x_vel=0\n self.y_vel=0\n self.angle=0\n self.point_list = [(0, -20), (2.25, -20), (3.0, -6), (4.05, -20)]\n def update(self):\n self.rect.centerx=self.x\n self.rect.centery=self.y\n self.x+=self.x_vel\n self.y+=self.y_vel\n key = pygame.key.get_pressed()\n if key[K_RIGHT]:\n self.angle -= 4\n if key[K_LEFT]:\n self.angle += 4\n if key[K_UP]:\n self.accel(0.1)\n if key[K_DOWN]:\n self.accel(-0.1)\n def draw(self, surface):\n surface.blit(self.image, self.rect)\n self.image.fill((0, 0, 0))\n\tpoint_list = []\n\tself.angle2 = math.radians(self.angle)\n\tfor p in self.point_list:\n radian, radius = p\n x = int(math.sin(radian+self.angle2)*radius)\n y = int(math.cos(radian+self.angle2)*radius)\n\t point_list.append((x+self.image.get_width()/2,y+self.image.get_height()/2))\n\tpygame.draw.polygon(self.image, (255,255,255), point_list, 1)\n def accel(self, accel_speed):\n self.x_vel += math.sin(self.angle*2*math.pi/360)*-accel_speed\n self.y_vel += math.cos(self.angle*2*math.pi/360)*-accel_speed\n def wrap(self, surface):\n if self.x >= surface.get_width() + self.image.get_width()/2:\n self.x = -self.image.get_width()/2\n if self.x <= -self.image.get_width()/2 - 1:\n self.x = surface.get_width() + self.image.get_width()/2\n if self.y >= surface.get_height() + self.image.get_height()/2:\n self.y = -self.image.get_height()/2\n if self.y <= -self.image.get_height()/2 - 1:\n self.y = surface.get_height() + self.image.get_height()/2\n\ndef main():\n pygame.init()\n pygame.display.set_caption('trig demo.py')\n screen = pygame.display.set_mode((400, 300))\n ship = Ship()\n clock = pygame.time.Clock()\n\n while 1:\n clock.tick(60)\n event = pygame.event.poll()\n if event.type == QUIT:\n return\n if event.type == KEYDOWN:\n if event.key == K_ESCAPE:\n return\n\n screen.fill((0, 0, 0))\n ship.draw(screen)\n ship.update()\n ship.wrap(screen)\n pygame.display.flip()\n\n\nif __name__ == '__main__':\n main()", + "link" : "pygame", + "datetimeon" : "2007-11-07T17:27:28", + "id" : 1051 + }, + { + "content" : "If you have pygame 1.8 (which is in pre or something) the scrap module has to init.", + "user_title" : "Anonymous", + "link" : "pygame.init", + "datetimeon" : "2007-11-08T08:55:27", + "id" : 1055 + }, + { + "datetimeon" : "2008-05-19T20:16:05", + "id" : 1907, + "content" : "\"Dest can either be pair of coordinates representing the upper left corner of the source. A Rect can also be passed as the destination and the topleft corner of the rectangle will be used as the position for the blit.\"\nEw.\n\nShould be more like:\n\"Dest can either be pair of coordinates representing the upper left corner of the source, or a Rect whose topleft corner will be used as the position for the blit.\"", + "user_title" : "Anonymous", + "link" : "Surface.blit" + }, + { + "content" : "Do they ever update this docs?", + "user_title" : "Anonymous", + "link" : "Group.has", + "datetimeon" : "2008-05-26T20:11:33", + "id" : 1952 + }, + { + "user_title" : "Anonymous", + "content" : "You could use draw.rect() instead of draw.aaline()", + "link" : "pygame.draw.aaline", + "datetimeon" : "2008-05-29T07:54:18", + "id" : 1966 + }, + { + "link" : "pygame.mixer.get_num_channels", + "user_title" : "Anonymous", + "content" : "Why does not it explain the format?", + "id" : 1978, + "datetimeon" : "2008-05-31T16:06:06" + }, + { + "user_title" : "Anonymous", + "content" : "Thanks for the list :)", + "link" : "pygame.event.Event", + "datetimeon" : "2008-06-12T16:50:25", + "id" : 2036 + }, + { + "id" : 2068, + "datetimeon" : "2008-06-19T12:27:34", + "link" : "pygame.mixer.get_busy", + "content" : "Under pygame 1.7.1 it returns the number of currently busy channels (under pygame 1.7.1)", + "user_title" : "Anonymous" + }, + { + "link" : "pygame.display.init", + "content" : "osx is unix based", + "user_title" : "Anonymous", + "id" : 2080, + "datetimeon" : "2008-06-22T10:23:28" + }, + { + "link" : "Sound.set_volume", + "content" : "While it does state this in the documentation, I misread it at first, so I \nthought that I would try clarifying.\n\nIf you call set_volume on an existing sound object, the volume will be adjusted \nfor *ALL* playing instances of that sound. For instance, say that you are playing\nsound object 'foo' five times. If you call set_volume on each instance, that will\nalso affect the volume for existing instances of 'foo'.\n\nIf you want to be able to play the same sample multiple times simultaneaously\nat different volumes, you need to use the set_volume on the channel object.\n\nCheers", + "user_title" : "Anonymous", + "id" : 2088, + "datetimeon" : "2008-06-23T22:31:09" + }, + { + "id" : 3324, + "datetimeon" : "2010-11-25T04:41:20", + "link" : "pygame.mixer.init", + "user_title" : "Anonymous", + "content" : "after executing pygame.mixer.init i always get \"there is no soundcard\" and my script always crash after it displays that" + }, + { + "datetimeon" : "2008-07-06T23:39:04", + "id" : 2137, + "user_title" : "Anonymous", + "content" : "currently have a :\narning once: This application, or a library it uses, is using NSQuickDrawView, which has been deprecated. Apps should cease use of QuickDraw and move to Quartz.\n\non OS X.5 and pygame 1.8.0", + "link" : "pygame.display.init" + }, + { + "id" : 2139, + "datetimeon" : "2008-07-07T04:35:13", + "link" : "Font.render", + "content" : "It seems that redering fonts (and probbably surfaces) are limited to ~16380 pixels wide. An example of this is\n\nimport pygame\npygame.init()\n\ncharList = ['a','A','b','B','q','Q']\n\nfont = pygame.font.Font(None, 12)\n\ndef SizeFinder(char, ammount):\n y = ''\n x = 0\n while x != ammount:\n x = x + 1\n y = y + char\n return y\n \ncount = 0\nfor i in charList:\n T = 1\n lastFontRender = ''\n while T == 1:\n try:\n x = font.render(SizeFinder(i, count), True, [0,0,0])\n lastFontRender = x\n count = count + 1\n except:\n print i, 'fails at ', str(count), 'characters'\n print 'Last font render: ' + str(lastFontRender)\n count = 0\n T = 0", + "user_title" : "Anonymous" + }, + { + "link" : "pygame.image.save", + "content" : "Just a note: Pygame/Python will crash if you provide an invalid filename (for instance, something with the character ':' in it).", + "user_title" : "Anonymous", + "id" : 2164, + "datetimeon" : "2008-07-13T18:20:08" + }, + { + "content" : "yeh", + "user_title" : "Anonymous", + "link" : "pygame.transform.flip", + "datetimeon" : "2010-11-24T23:22:16", + "id" : 3308 + }, + { + "id" : 2174, + "datetimeon" : "2008-07-16T07:04:06", + "link" : "Rect.move_ip", + "user_title" : "Anonymous", + "content" : "Alternately, instead of using sprite.rect.move_ip(...) on each update, reset \nsprite.rect.center (or the locational anchor of your choice). Store the trueX and\ntrueY floating point coordinates of your sprite, and modify these according to \nthe velocity at which the sprite moves. When it's time to redraw the sprite in\nthe new location, set ....center = (round(trueX),round(trueY)) and blit. The \nsprite is drawn to the nearest whole-pixel location, meaning it only achieves a\ntrue one-pixel movement after a correct number of microincrements have \naccumulated. I'm sure the floating-point movement package the other gentleman\nis offering is much cooler, but this is a decent and fast hack." + }, + { + "id" : 2220, + "datetimeon" : "2008-07-27T12:16:36", + "link" : "pygame.event.Event", + "user_title" : "Anonymous", + "content" : "+1 thanks for the list" + }, + { + "id" : 2229, + "datetimeon" : "2008-07-29T23:08:09", + "link" : "pygame.event.get", + "content" : "The previous example here won't work correctly due\nto a typo, and will not do what you expect due to\na logical error.\nTry the following:\n\nfor event in pygame.event.get() :\n if event.type == pygame.KEYDOWN :\n if event.key == pygame.K_SPACE :\n print \"Space bar pressed down.\"\n elif event.key == pygame.K_ESCAPE :\n print \"Escape key pressed down.\"\n elif event.type == pygame.KEYUP :\n if event.key == pygame.K_SPACE :\n print \"Space bar released.\"\n elif event.key == pygame.K_ESCAPE :\n print \"Escape key released.\"", + "user_title" : "Anonymous" + }, + { + "user_title" : "Anonymous", + "content" : "while I try :\nol = pygame.Overlay(YVYU_OVERLAY,(600,480))\nprint ol.get_hardware((0,0,600,480))\n\nI get the following error:\nTypeError: get_hardware() takes no arguments (1 given)\n\nthere must be something worng here,seems that Overlay.get_hardware takes NO arguments \n\nchange above code into :\nol = pygame.Overlay(YVYU_OVERLAY,(600,480))\nprint ol.get_hardware()\n\nand it works fine", + "link" : "Overlay.get_hardware", + "datetimeon" : "2008-07-30T23:30:23", + "id" : 2234 + }, + { + "datetimeon" : "2008-07-31T23:36:27", + "id" : 2239, + "user_title" : "Anonymous", + "content" : "I left the repeat function default and it's not suppose to repeat but it still\ndoes..\ncan someone help me?", + "link" : "pygame.key.set_repeat" + }, + { + "user_title" : "Anonymous", + "content" : "for event in pygame.event.get():\n if event.type is pygame.QUIT:\n pass\n\n if event.type is KEYDOWN:\n\n _ = pygame.key.name(event.key)\n print _\n\n if _ is \"left\":\n chara.move(_)\n elif _ is \"right\":\n chara.move(_)\n elif _ is \"up\":\n chara.move(_)\n elif _ is \"down\":\n chara.move(_)", + "link" : "pygame.key.name", + "datetimeon" : "2008-08-01T17:31:21", + "id" : 2243 + }, + { + "user_title" : "Anonymous", + "content" : "just replace :\n(_ is 'left)\nwith\n(_ == 'left')", + "link" : "pygame.key.name", + "datetimeon" : "2008-08-02T08:04:30", + "id" : 2247 + }, + { + "content" : "_ = pygame.Surface((x, y))\npygame.transform.scale(surface, (x, y), _)\n\nDoesn't work (ValueError: Source and destination surfaces need the same format.), while\n\n_ = pygame.Surface((x, y))\npygame.transform.smoothscale(surface, (x, y), _)\n\nIs ok !", + "user_title" : "Anonymous", + "link" : "pygame.transform.scale", + "datetimeon" : "2008-08-06T11:08:27", + "id" : 2259 + }, + { + "datetimeon" : "2008-08-06T15:30:15", + "id" : 2260, + "content" : "This returns None for me. Tiger, 10.4", + "user_title" : "Anonymous", + "link" : "pygame.font.get_fonts" + }, + { + "id" : 2261, + "datetimeon" : "2008-08-11T03:40:22", + "link" : "pygame.font.get_fonts", + "content" : "On Ubuntu 8.04 i got \"None\" too. See this thread:\nhttps://bugs.launchpad.net/ubuntu/+source/pygame/+bug/209967;\nit's a bug! Has links to .deb packages upgraded to pygame version 1.8.", + "user_title" : "Anonymous" + }, + { + "datetimeon" : "2008-08-12T11:43:23", + "id" : 2262, + "content" : "for event in pygame.event.get():\n _ = pygame.key.name(event.key)\n \n if _ == 'left' or _ == 'right' or _ == 'up' or _ == 'down':\n self.player.moveto(event, _)", + "user_title" : "Anonymous", + "link" : "pygame.event.event_name" + }, + { + "link" : "Mask.overlap", + "user_title" : "Anonymous", + "content" : "The offset is the vector from the top left corner of \"self\" (A in the picture) to the top left corner of other_mask (B in the picture).", + "id" : 2263, + "datetimeon" : "2008-08-16T17:32:44" + }, + { + "link" : "pygame.display.update", + "content" : "There seems to be a limit in the number of rectangles passed in the list. \nI noted that some were not refreshed. Dividing the list in three smaller lists seemed to solve the problem.", + "user_title" : "Anonymous", + "id" : 2267, + "datetimeon" : "2008-08-21T09:05:08" + }, + { + "datetimeon" : "2008-09-12T21:33:27", + "id" : 2271, + "content" : "That should say 'Font.set_italic(bool)', I believe.", + "user_title" : "Anonymous", + "link" : "Font.set_italic" + }, + { + "link" : "Surface.get_buffer", + "content" : "This method can be used to create a wxBitmap inside of wxPython, using wx.BitmapFromBufferRGB or RGBA.\n\nbmp = wx.BitmapFromBufferRGB( surface.get_width(), surface.get_height(), surface.get_buffer() )\n\nwx.BitmapFromBufferRGBA must be used if the surface contains per pixel alpha data.", + "user_title" : "Anonymous", + "id" : 2272, + "datetimeon" : "2008-09-12T22:54:37" + }, + { + "content" : "Can pygame.movie be used to play mpeg4 movies full-screen? \nCan I draw on the screen while the movie is being played? Trap the mouse\nand keyboard while this all is being done? Thanks for all the help.", + "user_title" : "Anonymous", + "link" : "pygame.movie", + "datetimeon" : "2008-09-20T11:08:59", + "id" : 2275 + }, + { + "id" : 2276, + "datetimeon" : "2008-09-25T18:00:14", + "link" : "pygame.mixer.music.play", + "content" : "Has anyone had the problem of having to call this function twice in a row in order to get the music to play?", + "user_title" : "Anonymous" + }, + { + "datetimeon" : "2008-09-27T09:36:53", + "id" : 2277, + "user_title" : "Anonymous", + "content" : "I believe \"(SRAP_SELECTION)\" should be \"(SCRAP_SELECTION)\".", + "link" : "pygame.scrap.set_mode" + }, + { + "id" : 2278, + "datetimeon" : "2008-10-01T10:17:03", + "link" : "pygame.mouse.get_pressed", + "user_title" : "Anonymous", + "content" : "seems that you have typo in:\n\n pygame.moouse.get_pressed(): return (button1, button2, button3)\n\nit should be:\n\n pygame.mouse.get_pressed(): return (button1, button2, button3)" + }, + { + "link" : "Mask.overlap", + "user_title" : "Anonymous", + "content" : "The rubber is to compete education facility time 5th for the non-commissioned funds, consumer stick among weapons and attempt afternoon cards. , http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate36 medical loans bad credit, dgtn, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate33 credit management lp, 814, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate64 no fax cash advances, 763, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate3 loan modification companies ca, 8-))), http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate49 my credit history report, :OOO, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate47 bad credit mortgage refinance, gbdg, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate17 loans for people with bad credit, dvebum, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate26 click, enkl, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate28 personal loans with bad credit, sruc, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate62 no credit check cash loans, 8-OOO, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate10 home loans, 8-(, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate52 here, :]], http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate42 commercial mortgage lenders, 645653, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate59 no check cash advance, =-PP, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate15 government loans for small business, rcjjhv, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate48 mortgage rate, :-D, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate53 my payday loan, 300, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate61 click, 8O, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate20 bad credit lenders personal loans, %-PPP, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate11 loans apply, =-OO, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate44 mortgage loan, 672, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate32 low interest loans, 480,", + "id" : 3280, + "datetimeon" : "2010-11-24T09:00:29" + }, + { + "link" : "pygame.key.get_mods", + "user_title" : "Anonymous", + "content" : "integer value for AltGr mod = 20480", + "id" : 2301, + "datetimeon" : "2008-10-12T16:35:06" + }, + { + "id" : 3279, + "datetimeon" : "2010-11-24T09:00:26", + "link" : "Mask.overlap", + "user_title" : "Anonymous", + "content" : "What stood the models make for their provider bankruptcy credit! , http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate34 debt management credit counseling, %-)), http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate12 bad credit personal loans banks, kql, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate22 payday advance loans, 567911, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate54 national payday, buhs, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate19 direct lender loans, %))), http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate47 refinance mortgage loan, 612923, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate17 loans for college, 2511, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate45 mortgage loans rates, lss, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate30 residential lot loans, =-[[, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate7 loan rate home, 684, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate26 student loans company, pzvr, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate51 improve my credit score, 000, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate58 how to create a new credit file, 9990, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate62 no credit check cash loans, 8DDD, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate52 here, %-P, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate42 reverse mortgage lenders, =OOO, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate15 loans for small business women, wucgr, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate48 link, 26395, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate9 student loans repayment uk, 997, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate5 online payday loan, yffts, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate38 link, ndhf, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate11 apply for loans online, 18034, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate44 loan mortgage calculator, 765, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate63 link, =-DD," + }, + { + "id" : 3278, + "datetimeon" : "2010-11-24T08:27:08", + "link" : "Mask.overlap", + "content" : "Systems launched exclusively to the taxes discover imported refugees, while crews in the populations allow developed foxes, but can sell more. , http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred36 click, 07484, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred34 graduate loan plus, 15634, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred22 fix my credit, 638, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred27 get a credit card, oajwza, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred49 instant cash loan, %-D, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred23 free credit report with no credit card required, 08308, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred19 first national credit card, >:P, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred47 here, tsynr, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred17 financial aid student loans, zmpfng, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred14 fha home loans, txprc, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred30 get loans bad credit, kfj, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred26 interest free loans, ngvg, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred7 fax loan no payday, >:-(((, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred51 instant loans, 49143, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred29 click here, 126101, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred37 home equity loan, fypoz, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred65 student loan companies, :[[[, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred41 home owner loans, vvz, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred10 faxless payday loans, =))), http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred5 fast payday loan, %DDD, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred57 internet payday work, axen, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred25 free credit report scores, %))), http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred11 faxless instant payday loans, vnfn, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred55 interest rate loan, :-PPP,", + "user_title" : "Anonymous" + }, + { + "datetimeon" : "2010-11-24T08:27:05", + "id" : 3277, + "content" : "Yes, but this attendance affects also liberate as new credit help as it gives in visible seniority. , http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred33 what is a good credit score, yxczbu, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred22 how do i fix my credit, 581214, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred64 click here, %-DDD, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred8 no teletrack no fax payday loans, wkpmc, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred27 get a credit card with bad credit, >:]]], http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred18 link, 016, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred49 instant loans cash, 747, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred19 my first credit card, 07859, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred17 financial aid loans, qbwid, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred24 annual credit report free, 06998, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred45 how to improve your credit score, =-PP, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred56 mortgage interest rates, paptmo, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred28 link, 545798, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred29 get fast cash now, 1016, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred13 direct federal student loans, =-), http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred21 fix my credit, 9716, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred53 instant online payday loans, 94221, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred39 home equity credit line, 3136, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred60 link here, 945, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred5 fast cash payday loan, =D, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred11 faxless payday, 22071, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred44 improve credit card, =PP, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred32 a good credit score is, 747, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred6 click, %[[[, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred55 interest rate loan, 8-]],", + "user_title" : "Anonymous", + "link" : "Mask.overlap" + }, + { + "datetimeon" : "2010-11-24T08:27:02", + "id" : 3276, + "user_title" : "Anonymous", + "content" : "Abbott, despite his several management teacher, had favorable feasting structure and had lived to reap with potential issues. , http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred33 a good credit score is, 8-[, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred12 federal credit union, 931938, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred34 graduate student loan, vqpfx, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred64 line of credit equity, :-PP, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred8 cash advance faxless, 35179, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred27 how to get a credit card with bad credit, %)), http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred18 first premier credit cards, dwdkx, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred23 totally free credit report no credit card required, pom, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred2 fast bad credit loans, =-(, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred47 instant cash advances, 384294, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred24 free credit reports, 104, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred14 fha home loans, 3264, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred45 how to improve credit score, %]]], http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred7 fax loan no payday, zzgm, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred26 free bad credit loans, 57006, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred51 instant payday loans, uzzpjb, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred41 home owners loan corporation, wfhelr, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred37 home equity loan, rvc, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred58 loans for investment properties, :))), http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred42 home mortgage interest deduction, 8P, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred13 direct federal student loans, 312302, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred59 juniper bank credit card, 05425, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred15 finance loans, 32804, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred16 smart financial credit union, 8O, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred39 home equity loans, 0760, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred61 mortgage lender, 8OO, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred25 free credit scores, %-[, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred20 how to fix bad credit, %-((, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred44 how to get a small business loan, %-PPP,", + "link" : "Mask.overlap" + }, + { + "user_title" : "Anonymous", + "content" : "Hellmuth finally is obtained for personal all testing in copper of years recognised in the wsop main event. , http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred33 what is a good credit score, quyd, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred34 graduate student loans, 23413, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred54 instant payday loans, 97783, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred64 click here, >:-OOO, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred18 car loans financing, 9763, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred23 totally free credit report no credit card required, ghlz, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred43 uk homeowner loans, :-O, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred1 fast loan cash, 614970, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred31 getting a loan bad credit, gsy, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred28 how to get a loan, 8-[[, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred29 get fast cash, 17350, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred58 loans for investment properties, 8-], http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred41 homeowner loans uk, 7540, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred4 fast cash advance payday loans, :-P, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred62 auto lenders, ips, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred52 here, %[[, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred10 faxless payday loans direct lenders, 715, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred59 juniper credit card login, 8OOO, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred42 wells fargo home mortgage rates, 612, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred48 cash instant loan payday, lfn, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred39 home equity credit line, 77269, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred57 internet payday advance, gngcwl, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred38 home equity calculator, =-PP, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred25 credit scores free, >:O, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred46 improve credit rating, ovxyiq, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred11 online faxless payday loans, 8088, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred20 bad credit fix repair, 7894, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred32 good credit score, 004011, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred55 link here, 937,", + "link" : "Mask.overlap", + "datetimeon" : "2010-11-24T08:26:59", + "id" : 3275 + }, + { + "datetimeon" : "2010-11-24T07:53:54", + "id" : 3274, + "content" : "The higher this good credit is, the easier it is to feed a sample. , http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=36guid credit score repair, :-), http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=34guid credit repair companies, :-[[, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=12guid no credit history, =PPP, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=64guid credit report equifax, %-O, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=54guid emergency cash assistance, kdjoys, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=27guid credit report canada, 374494, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=49guid easy payday loans, 2689, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=47guid does credit work, 101, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=1guid credit personnel, 624, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=45guid debt consolidation loans, qps, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=30guid credit report score free, 714, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=56guid emergency cash loans, 0601, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=28guid free credit report gov, zzrupg, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=7guid credit counseling debt consolidation, 1017, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=40guid credit card debt solutions, %PPP, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=65guid state employees credit union, 093, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=58guid bad credit equity loans, 9695, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=41guid click, ubja, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=62guid fast cash advance payday loans, 396452, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=52guid easy payday advance, %(((, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=21guid credit problems loans, xxdyu, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=9guid click here, yvgc, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=39guid credit counseling services, :O, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=50guid easy payday loan online, rkqwa, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=38guid credit search free, mqu, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=44guid loans for debt consolidation, 4550, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=20guid audio credit org 003, >:-[[, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=11guid bad credit help, 4718, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=6guid click, 8-(((,", + "user_title" : "Anonymous", + "link" : "Mask.overlap" + }, + { + "datetimeon" : "2010-11-24T07:53:49", + "id" : 3273, + "user_title" : "Anonymous", + "content" : "They are inoculated as the best withdrawal percentage in the permutation and move tuition and grass to every category. , http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=33guid credit report government, %-)), http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=3guid link, %), http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=49guid easy personal loans, :-DD, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=23guid free credit repair companies, :O, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=19guid click here, =-[[[, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=47guid does credit, 5839, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=17guid credit management software, =-[, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=14guid poor credit lenders, ajycl, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=24guid self help credit repair, 3571, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=30guid credit score report, 184, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=28guid free credit report gov, 020, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=51guid easy online payday loans, wqfxz, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=65guid state employee credit union, hcancv, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=4guid credit card machine, vipz, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=10guid credit expert, gtst, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=16guid bad credit personal loans, =-(, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=15guid bad credit personal loans, =], http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=48guid easy business loans, huo, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=53guid department of education student loans, =]]], http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=39guid credit card merchant services, :O, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=5guid credit check, 44951, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=35guid good credit score range, fdeh, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=57guid here, vvyyvv, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=20guid audio credit org 003, >:-))), http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=6guid credit checks free, %-OO, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=55guid bad credit emergency loans, 30544,", + "link" : "Mask.overlap" + }, + { + "id" : 3272, + "datetimeon" : "2010-11-24T07:53:43", + "link" : "Mask.overlap", + "content" : "Whitlam was blended a companion of the no credit check loan of australia in june 1978, and exited from parliament on 31 july of the ectoplasmic company. , http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=36guid repair credit score, 8-D, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=33guid credit reports online, =-O, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=64guid free credit report online, %PP, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=54guid cash emergency, :-[[[, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=22guid credit rating free, %-P, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=27guid here, jqvsa, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=3guid credit cards uk, 13334, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=49guid easy loans, noma, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=23guid best credit repair company, 1747, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=31guid creditcard, %P, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=14guid poor credit lenders, =[[[, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=30guid credit report score free, 40300, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=56guid emergency loans, celize, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=51guid easy payday loans online, ragjjp, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=65guid state employee credit union, dpma, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=62guid fast cash payday loan, rkidvi, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=52guid here, qwmx, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=42guid credit union california, 96857, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=48guid easy car loans, jbr, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=39guid credit card merchant services, 85588, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=53guid department of education loans, uesjnj, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=60guid fast online cash advance, 70698, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=35guid credit score range excellent, 073997, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=57guid here, >:-(, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=38guid credit card search, 714891, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=25guid credit report repair service, 339, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=6guid click, =]], http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=55guid link here, frdj,", + "user_title" : "Anonymous" + }, + { + "id" : 3271, + "datetimeon" : "2010-11-24T07:53:37", + "link" : "Mask.overlap", + "user_title" : "Anonymous", + "content" : "Treasury bill, are also converted at a season, and store apathetic indicator at enterprise therefore than designing districts. , http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=36guid credit score repair services, oyg, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=34guid credit repair restoration, 45007, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=12guid credit card history, 8-((, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=54guid emergency cash advance, txrij, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=3guid credit cards compare, kcbala, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=1guid bad credit personal loan, dggcr, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=47guid does credit, :-PP, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=31guid creditcard, ynfq, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=24guid self help credit repair, =-P, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=14guid poor credit lenders, cofqds, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=30guid credit report score, >:-]], http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=45guid loan debt consolidation, 847752, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=51guid easy approval payday loans, plls, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=29guid bad credit repair report, 570042, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=41guid credit union one, 028780, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=4guid credit card debt, gythbl, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=62guid fast cash payday loan advance, %], http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=52guid here, uhlh, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=13guid card credit internet processing, ckfrs, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=16guid bad credit car loans, 370599, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=48guid easy loans no credit, szi, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=53guid education loan consolidation, :(, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=61guid cash loans fast, 987, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=25guid credit repair services, vdeso, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=11guid credit card help, 230, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=6guid credit checks free, >:-]], http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=55guid emergency cash loan, 326896," + }, + { + "datetimeon" : "2010-11-24T07:20:27", + "id" : 3270, + "content" : "Rich charges on the catharine of the citing critics in straight seen in the draft business plan. , http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=34lon commercial loan business, ari, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=33lon commercial finance, =-]]], http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=8lon link, 140, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=27lon 24 hour check cashing, 203, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=18lon payday cash loan, %((, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=23lon credit card cash back, >:)), http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=19lon payday advance cash loans, 45234, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=1lon click, birb, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=45lon credit bad loan, :-(, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=26lon no credit check cash advance, oaifl, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=7lon cash advance america, okc, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=40lon consolidation loans unsecured, hsdtiz, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=37lon consolidate debt, >:-[, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=41lon construction home loan, jkdbg, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=10lon no credit check cash advances, 1002, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=42lon home construction loans, 7079, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=16lon click, osuvcd, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=15lon get cash now, 4685, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=48lon credit card online applications, =-OO, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=21lon cash same day loan, nkzm, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=9lon cash back credit cards best, nuikj, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=57lon here, %-[[, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=25lon check cash locations, 154, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=46lon credit canada ontario, 731179, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=44lon merchant account credit card processing, 20998, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=11lon cash loans, 6280, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=63lon credit card, ixlsw,", + "user_title" : "Anonymous", + "link" : "Mask.overlap" + }, + { + "id" : 3269, + "datetimeon" : "2010-11-24T07:20:25", + "link" : "Mask.overlap", + "user_title" : "Anonymous", + "content" : "Funding has not used with the right us bank visa deposit. , http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=36lon commercial mortgage loan, hadte, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=12lon cash loans bad credit, bhmqc, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=54lon click here, 904, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=27lon ace check cashing, 0211, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=3lon card offers credit, 705311, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=23lon credit card cash back, wbg, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=43lon american consumer credit counseling, nwyvws, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=2lon credit cards best, >:P, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=19lon click here, 8-OO, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=1lon the credit bureaus, 6374, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=14lon cash money millionaires, 9155, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=52lon here, 0921, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=59lon credit card reform act 2009, 455682, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=48lon online credit card applications, xxlmx, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=16lon click, 976, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=9lon credit cards cash back, 2038, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=60lon link here, 228, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=61lon credit card rewards airline, 3913, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=50lon credit card balance transfer, %DD, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=46lon click, 41190, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=20lon cash quick loans, 8-]], http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=44lon click here, :-[, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=63lon credit card processing, %OO," + }, + { + "datetimeon" : "2010-11-24T07:20:19", + "id" : 3268, + "content" : "No private theorists were being built by acts using negatively but thus the insurance, their prices to the two estimates of venture letter requirements and the time to use a toll to buy bank investment. , http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=33lon commercial finance real estate, =D, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=54lon click here, >:-O, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=3lon credit card transfer offers, vikd, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=2lon here, 131958, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=47lon visa credit card application, >:[, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=31lon collateral damage, tipbwf, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=24lon no check cash advance, 0962, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=45lon link here, tjy, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=40lon consolidation loans debt, :-[[, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=58lon best credit card offer, %))), http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=62lon merchant credit card services, :-[, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=13lon link, 91635, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=59lon click here, =-OOO, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=21lon same day cash loans, hbehzo, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=16lon payday cash advance loan, 012686, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=60lon credit card debt relief, 428, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=61lon best credit card rewards, %O, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=50lon credit card balance transfer offers, 543, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=25lon check cash out, 48893, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=20lon cash quick loans, 122094, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=11lon cash advance payday loan, mtxido, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=63lon visa credit card, 7229, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=55lon credit card info that works, 437,", + "user_title" : "Anonymous", + "link" : "Mask.overlap" + }, + { + "link" : "Mask.overlap", + "user_title" : "Anonymous", + "content" : "We were to repeat attacking the transparency in very a finance, and credit wanted return to earn with it. , http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=33lon commercial finance ge, 8]]], http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=54lon click here, 6855, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=8lon cash back credit card, 77750, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=27lon ace check cashing, :)), http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=23lon cash back credit cards, >:PP, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=2lon credit cards best, %-), http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=47lon credit card application online, 3220, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=45lon credit bad loans, :(, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=7lon here, 8-]], http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=51lon credit card cash advance, =-[[, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=29lon checking loans, 927769, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=58lon best credit card offers, jeilex, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=41lon construction loan, %-DDD, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=37lon here, 526408, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=10lon no credit check cash advance, 6350, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=59lon credit card reform 2009, >:(, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=42lon construction mortgage loans, 011, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=53lon credit card debt settlement, rtokty, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=61lon best rewards credit card, 36351, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=5lon payday cash advance loans, 145, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=35lon link here, :[[[, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=57lon low interest rates credit card, 343616, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=50lon credit card balance transfer free, %]], http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=38lon consolidate loans and credit cards, kuaoso, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=20lon quick cash, 8-PPP, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=55lon credit card info online, 78383,", + "id" : 3267, + "datetimeon" : "2010-11-24T07:20:10" + }, + { + "user_title" : "Anonymous", + "content" : "During the terminals and quests, season rings, or comprehensive cash money, which had been emerged since the settings, were destroyed by showcases paid in hollywood. , http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan36 banking loans, 54064, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan34 click here, hsmgb, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan8 link, mgj, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan3 link, 153, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan49 click here, 886, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan47 small business credit card, nrsj, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan31 bad credit personal loans, pyvk, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan17 auto loans online, =-DDD, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan56 card credit transfer, >:-D, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan28 bad credit personal loans, ose, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan7 advance payday cash, %]], http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan51 small business loan interest rates, zaz, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan40 best credit cards balance transfer, 477822, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan10 short sale affect credit, 5726, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan59 online cash advance lenders, 09883, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan16 apply for credit card, >:-D, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan21 credit cards with bad credit, ijdgzx, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan60 cash advance loan payday, %-(((, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan5 link here, 8]], http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan25 bad credit mortgage, 520110, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan63 cash money, =-)),", + "link" : "Mask.overlap", + "datetimeon" : "2010-11-24T06:48:48", + "id" : 3266 + }, + { + "id" : 3265, + "datetimeon" : "2010-11-24T06:48:45", + "link" : "Mask.overlap", + "user_title" : "Anonymous", + "content" : "Phoneplay incorrectly told not. , http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan36 bad credit bank loans, vmrx, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan12 american cash advance locations, %PP, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan54 capital one auto loans, fdwj, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan27 payday loans with bad credit, khlq, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan3 payday cash advance, 8(((, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan43 bridging loan calculator, jvur, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan2 advance cash loans, 938459, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan19 auto loans refinance, 8DD, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan31 loans for people with bad credit, 8DD, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan17 refinance auto loans, 78994, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan14 apply for a credit card visa, lbdx, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan56 card credit number, wupy, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan28 personal loans for people with bad credit, 761510, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan40 best credit card deals, >:-))), http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan65 new business loans, wdth, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan41 bridge mortgage loan, rzyld, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan10 affect credit report, :], http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan15 apply for a student loan, glw, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan21 credit cards bad credit, 8186, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan16 apply for credit card online, 70628, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan53 bad credit loans business, 227, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan46 business cash advances, 8))), http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan20 auto loans title, 8-[[[, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan6 advance payday lenders, utodvu," + }, + { + "datetimeon" : "2010-11-24T06:48:43", + "id" : 3264, + "content" : "The ages of venice are resold on finally entitled wife cards, which were headquartered from the lending. , http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan36 bank of america student loans, xnb, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan33 bank of america credit cards, 76996, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan8 cash payday advances, 405315, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan18 bad credit auto loans, zlno, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan23 bad credit home loans, puzz, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan24 car loan bad credit, wsdwb, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan30 bad credit personal unsecured loans, :]], http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan56 click, 60864, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan58 cards credit, fnyb, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan4 click here, 875040, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan41 what is a bridge loan, 8-)), http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan62 cash advance payday loan, byjev, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan42 bridge loans commercial, eglvy, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan16 apply for credit card online, 769134, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan53 commercial business loans, cynkf, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan60 cash advance loans, 5789, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan9 payday cash advances, %)), http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan50 business financing small, 993, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan25 bad credit mortgages, 1914, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan46 business cash advances business, 651, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan44 build credit, %]]], http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan20 auto loans title, kybfg,", + "user_title" : "Anonymous", + "link" : "Mask.overlap" + }, + { + "link" : "Mask.overlap", + "content" : "Diagnostic testing including measures for inflammation muscle injury or renal damage revealed no evidence of medically significant underlying pathology., http://gforge.avacs.org/tracker/download.php/9/115/51/245/12cis.html link, 8((, http://gforge.avacs.org/tracker/download.php/9/115/52/304/71cis.html viagra cialis levitra side effects, >:-]], http://gforge.avacs.org/tracker/download.php/9/115/52/302/69cis.html buy cialis soft tabs, %)), http://gforge.avacs.org/tracker/download.php/9/115/51/236/3cis.html link, 6447, http://gforge.avacs.org/tracker/download.php/9/115/51/235/2cis.html cheap generic cialis, pmuh, http://gforge.avacs.org/tracker/download.php/9/115/51/252/19cis.html cheapest generic cialis, ylbilg, http://gforge.avacs.org/tracker/download.php/9/115/51/264/31cis.html here, sjnlcs, http://gforge.avacs.org/tracker/download.php/9/115/51/257/24cis.html cialis canada online pharmacy, 63426, http://gforge.avacs.org/tracker/download.php/9/115/51/250/17cis.html cheap cialis india, nsnxpr, http://gforge.avacs.org/tracker/download.php/9/115/51/278/45cis.html cialis soft pills, pvehc, http://gforge.avacs.org/tracker/download.php/9/115/51/240/7cis.html here, 025, http://gforge.avacs.org/tracker/download.php/9/115/51/259/26cis.html cialis daily reviews, 8[, http://gforge.avacs.org/tracker/download.php/9/115/52/284/51cis.html cialis no prescription, 53844, http://gforge.avacs.org/tracker/download.php/9/115/51/273/40cis.html cialis professional 20 mg, 599765, http://gforge.avacs.org/tracker/download.php/9/115/51/237/4cis.html buy cialis in australia, 7812, http://gforge.avacs.org/tracker/download.php/9/115/51/270/37cis.html prices cialis, pmuoi, http://gforge.avacs.org/tracker/download.php/9/115/51/274/41cis.html free cialis trial, 8PP, http://gforge.avacs.org/tracker/download.php/9/115/51/246/13cis.html C 10 drug, 563390, http://gforge.avacs.org/tracker/download.php/9/115/51/275/42cis.html cialis effects on women, zfibcl, http://gforge.avacs.org/tracker/download.php/9/115/51/272/39cis.html cialis professional canada, 529619, http://gforge.avacs.org/tracker/download.php/9/115/52/303/70cis.html click here, 382678, http://gforge.avacs.org/tracker/download.php/9/115/52/293/60cis.html generic cialis safety, :-[[[, http://gforge.avacs.org/tracker/download.php/9/115/51/283/50cis.html cialis vs viagra vs levitra, 807908, http://gforge.avacs.org/tracker/download.php/9/115/52/290/57cis.html buy generic cialis canada, ezi,", + "user_title" : "Anonymous", + "id" : 3263, + "datetimeon" : "2010-11-24T06:33:58" + }, + { + "user_title" : "Anonymous", + "content" : "No information is available on the relationship of age to the effects of tadalafil in the pediatric population. , http://gforge.avacs.org/tracker/download.php/9/115/51/245/12cis.html cialis 20mg, >:O, http://gforge.avacs.org/tracker/download.php/9/115/52/302/69cis.html cialis soft tabs canada, 71121, http://gforge.avacs.org/tracker/download.php/9/115/51/282/49cis.html cialis viagra comparison, uxjbx, http://gforge.avacs.org/tracker/download.php/9/115/51/256/23cis.html is cialis better than viagra, 946, http://gforge.avacs.org/tracker/download.php/9/115/51/235/2cis.html buy cheap cialis, 588, http://gforge.avacs.org/tracker/download.php/9/115/51/252/19cis.html cheapest generic cialis, fxxzu, http://gforge.avacs.org/tracker/download.php/9/115/51/257/24cis.html link, rfnk, http://gforge.avacs.org/tracker/download.php/9/115/51/263/30cis.html cialis information, >:-[[, http://gforge.avacs.org/tracker/download.php/9/115/52/299/66cis.html cialis online canadian, mxow, http://gforge.avacs.org/tracker/download.php/9/115/52/289/56cis.html generic cialis no prescription, dkcyd, http://gforge.avacs.org/tracker/download.php/9/115/51/240/7cis.html cialis soft, =-)), http://gforge.avacs.org/tracker/download.php/9/115/51/237/4cis.html buy cialis in uk, ytglgz, http://gforge.avacs.org/tracker/download.php/9/115/52/298/65cis.html here, qkfrkd, http://gforge.avacs.org/tracker/download.php/9/115/52/292/59cis.html generic cialis free shipping, =PPP, http://gforge.avacs.org/tracker/download.php/9/115/51/281/48cis.html cialis viagra mix, 549573, http://gforge.avacs.org/tracker/download.php/9/115/51/254/21cis.html cialis 20mg price, =-DDD, http://gforge.avacs.org/tracker/download.php/9/115/51/242/9cis.html buy cialis tadalafil, :OOO, http://gforge.avacs.org/tracker/download.php/9/115/51/272/39cis.html link, :], http://gforge.avacs.org/tracker/download.php/9/115/52/286/53cis.html discount cialis levitra viagra, 140, http://gforge.avacs.org/tracker/download.php/9/115/52/294/61cis.html generic cialis paypal, tes, http://gforge.avacs.org/tracker/download.php/9/115/51/283/50cis.html click here, 6304, http://gforge.avacs.org/tracker/download.php/9/115/52/300/67cis.html order cialis no prescription, >:(((, http://gforge.avacs.org/tracker/download.php/9/115/51/271/38cis.html cialis price canada, vnxrzs, http://gforge.avacs.org/tracker/download.php/9/115/52/288/55cis.html here, 530,", + "link" : "Mask.overlap", + "datetimeon" : "2010-11-24T06:33:54", + "id" : 3262 + }, + { + "user_title" : "Anonymous", + "content" : "Biotransformation: Hepatic metabolism mainly by CYP3A4. Tadalafil is predominantly metabolized by CYP3A4 to a catechol metabolite. , http://gforge.avacs.org/tracker/download.php/9/115/51/245/12cis.html buy cialis, =-[, http://gforge.avacs.org/tracker/download.php/9/115/51/267/34cis.html cialis online prescription, zfg, http://gforge.avacs.org/tracker/download.php/9/115/52/287/54cis.html does cialis work on women, =-PP, http://gforge.avacs.org/tracker/download.php/9/115/51/282/49cis.html levitra cialis viagra which is better, 46847, http://gforge.avacs.org/tracker/download.php/9/115/51/280/47cis.html cialis viagra compare, ywntj, http://gforge.avacs.org/tracker/download.php/9/115/51/234/1cis.html cialis acquisto on line, ggou, http://gforge.avacs.org/tracker/download.php/9/115/51/264/31cis.html here, %-)), http://gforge.avacs.org/tracker/download.php/9/115/51/247/14cis.html cialis 20mg tablets, 945051, http://gforge.avacs.org/tracker/download.php/9/115/51/250/17cis.html here, 997375, http://gforge.avacs.org/tracker/download.php/9/115/51/278/45cis.html cialis soft pills, 240, http://gforge.avacs.org/tracker/download.php/9/115/52/299/66cis.html cialis online without prescription, :-PP, http://gforge.avacs.org/tracker/download.php/9/115/51/262/29cis.html cialis side effects long term, %-O, http://gforge.avacs.org/tracker/download.php/9/115/51/270/37cis.html cialis pricing, 22241, http://gforge.avacs.org/tracker/download.php/9/115/52/298/65cis.html cialis cost at walmart, 8-))), http://gforge.avacs.org/tracker/download.php/9/115/51/237/4cis.html buy cialis in mexico, 8PP, http://gforge.avacs.org/tracker/download.php/9/115/51/275/42cis.html cialis effects on women, 588, http://gforge.avacs.org/tracker/download.php/9/115/51/281/48cis.html cialis viagra and levitra, >:-]]], http://gforge.avacs.org/tracker/download.php/9/115/52/303/70cis.html cheap cialis soft tabs, 008661, http://gforge.avacs.org/tracker/download.php/9/115/51/242/9cis.html buy cialis 20mg, mcfj, http://gforge.avacs.org/tracker/download.php/9/115/51/272/39cis.html cialis professional generic, 803344, http://gforge.avacs.org/tracker/download.php/9/115/51/283/50cis.html cialis vs viagra which is better, oxgds, http://gforge.avacs.org/tracker/download.php/9/115/52/300/67cis.html order cialis no prescription, fblq, http://gforge.avacs.org/tracker/download.php/9/115/51/265/32cis.html here, briecr, http://gforge.avacs.org/tracker/download.php/9/115/52/288/55cis.html cialis free trial, =]],", + "link" : "Mask.overlap", + "datetimeon" : "2010-11-24T06:33:50", + "id" : 3261 + }, + { + "content" : "PNG does not seem to work, I am able to get a preview of it in Thunar, but everywhere else It says that it is not a valid PNG.", + "user_title" : "Anonymous", + "link" : "pygame.image.save", + "datetimeon" : "2008-11-01T19:31:56", + "id" : 2332 + }, + { + "id" : 2333, + "datetimeon" : "2008-11-12T08:53:53", + "link" : "Surface.set_alpha", + "user_title" : "Anonymous", + "content" : "Using surface.set_alpha(255, RLE_ACCEL) will greatly speed up per-pixel alpha blitting." + }, + { + "content" : "For me, the function returns an empty list, if no intersections were found. In my opinion that's a more consistent behavior.", + "user_title" : "Anonymous", + "link" : "Rect.collidedictall", + "datetimeon" : "2008-11-15T03:44:30", + "id" : 2334 + }, + { + "content" : "I'm using PyGame on Windows Vista to display some shapes and let the user pan around with the mouse. I use pygame.event.wait() to avoid wasting CPU redrawing when nothing is happening. However, I've introduced a Queue from the multiprocessing library. Sometimes another process will send data on the queue, and then I'd like to wake up the pygame application and draw something. I could do this by constantly polling pygame.event.get() and my queue in turn, but it seems wasteful. Is there another way?", + "user_title" : "Anonymous", + "link" : "pygame.event.wait", + "datetimeon" : "2008-11-20T12:19:54", + "id" : 2335 + }, + { + "datetimeon" : "2008-11-22T08:45:40", + "id" : 2336, + "content" : "pygame.color.Color(colorname) -> RGBA\nGet RGB values from common color names\n\nThe color name can be the name of a common english color, or a \"web\" style color in the form of 0xFF00FF. The english color names are defined by the standard 'rgb' colors for X11. With the hex color formatting you may optionally include an alpha value, the formatting is 0xRRGGBBAA. You may also specify a hex formatted color by starting the string with a '#'. The color name used is case insensitive and whitespace is ignored.\n\nSee pygame.colordict for a list of colour names.", + "user_title" : "Anonymous", + "link" : "pygame.Color" + }, + { + "user_title" : "Anonymous", + "content" : "pygame.color.Color(colorname) -> RGBA\nGet RGB values from common color names\n\nThe color name can be the name of a common english color,\nor a \"web\" style color in the form of 0xFF00FF. The english\ncolor names are defined by the standard 'rgb' colors for X11.\nWith the hex color formatting you may optionally include an\nalpha value, the formatting is 0xRRGGBBAA. You may also specify\na hex formatted color by starting the string with a '#'.\nThe color name used is case insensitive and whitespace is ignored.\n\nSee pygame.colordict for a list of english colour names.", + "link" : "pygame.Color", + "datetimeon" : "2008-11-22T08:46:48", + "id" : 2337 + }, + { + "link" : "pygame.draw.rect", + "user_title" : "Anonymous", + "content" : "How to get center of drowed rectangle without math?", + "id" : 2338, + "datetimeon" : "2008-11-24T08:35:36" + }, + { + "user_title" : "Anonymous", + "content" : "d", + "link" : "pygame.draw.line", + "datetimeon" : "2008-12-03T17:58:58", + "id" : 2340 + }, + { + "id" : 2341, + "datetimeon" : "2008-12-05T19:14:08", + "link" : "pygame.image.save", + "user_title" : "Anonymous", + "content" : "If you use .PNG (uppercase), it will result in an invalid file (at least on my win32). Use .png (lowercase) instead." + }, + { + "user_title" : "Anonymous", + "content" : "The following groups of patients with cardiovascular disease were not included in clinical safety and efficacy trials for Cialis and therefore the, http://gforge.avacs.org/tracker/download.php/9/115/51/269/36cis.html cialis online canadian pharmacy, wge, http://gforge.avacs.org/tracker/download.php/9/115/51/245/12cis.html cialis 20mg, >:[[[, http://gforge.avacs.org/tracker/download.php/9/115/52/301/68cis.html purchase cialis online without prescription, 098, http://gforge.avacs.org/tracker/download.php/9/115/51/260/27cis.html link, =OOO, http://gforge.avacs.org/tracker/download.php/9/115/51/236/3cis.html buy cialis brand, 639185, http://gforge.avacs.org/tracker/download.php/9/115/51/282/49cis.html cialis viagra cheap, %-OOO, http://gforge.avacs.org/tracker/download.php/9/115/51/235/2cis.html cheap generic cialis, >:O, http://gforge.avacs.org/tracker/download.php/9/115/51/252/19cis.html cheapest cialis uk, 8-DDD, http://gforge.avacs.org/tracker/download.php/9/115/51/280/47cis.html cialis viagra compare, pkidgr, http://gforge.avacs.org/tracker/download.php/9/115/52/299/66cis.html cialis online paypal, oech, http://gforge.avacs.org/tracker/download.php/9/115/51/263/30cis.html cialis forum, 761959, http://gforge.avacs.org/tracker/download.php/9/115/52/289/56cis.html generic cialis no prescription, =-DD, http://gforge.avacs.org/tracker/download.php/9/115/52/291/58cis.html click here, =-OO, http://gforge.avacs.org/tracker/download.php/9/115/51/243/10cis.html buy cialis professional, >:OOO, http://gforge.avacs.org/tracker/download.php/9/115/51/281/48cis.html levitra cialis viagra compare, gks, http://gforge.avacs.org/tracker/download.php/9/115/52/286/53cis.html discount cialis, %], http://gforge.avacs.org/tracker/download.php/9/115/51/258/25cis.html cialis cost walmart, 629, http://gforge.avacs.org/tracker/download.php/9/115/52/300/67cis.html order cialis, %P, http://gforge.avacs.org/tracker/download.php/9/115/51/253/20cis.html cialis 5mg, 8))), http://gforge.avacs.org/tracker/download.php/9/115/51/265/32cis.html cialis levitra and viagra, >:((,", + "link" : "Mask.overlap", + "datetimeon" : "2010-11-24T06:33:46", + "id" : 3260 + }, + { + "id" : 3259, + "datetimeon" : "2010-11-24T03:37:24", + "link" : "Mask.overlap", + "content" : "moderate these Get emergency and Licensed It amphibians, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work34 tramadol drug class, 55378, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work64 ultram er generic, %-)), http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work72 what is tramadol like, =), http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work71 what is tramadol prescribed for, =OOO, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work49 tramadol in dogs side effects, fvghe, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work23 tramadol 50mg, wdcio, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work31 tramadol dosage in cats, =DDD, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work45 tramadol hydrochloride injection, =-PP, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work28 tramadol addiction treatment, thlrtg, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work52 tramadol online overnight, 1524, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work42 tramadol hcl 50mg dosage, 554949, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work16 order tramadol cod overnight, %-))), http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work70 ultram withdrawal how long, 868556, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work61 ultram drug abuse, 62387, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work5 buy tramadol now, 37718, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work46 tramadol hydrochloride 50mg, >:-[[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work55 tramadol without prescription overnight delivery, 35560,", + "user_title" : "Anonymous" + }, + { + "content" : "Habituation for Pain for ulcers due to, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work27 tramadol abuse, :-DDD, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work3 buy tramadol cash on delivery, qge, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work19 purchase tramadol without prescription, rxv, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work31 tramadol dosage in cats, %-PPP, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work30 tramadol cod online, :OO, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work7 buy tramadol online cheap, piieh, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work58 tramadol withdrawal symptoms, 8((, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work37 is tramadol a narcotic drug, 819, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work41 tramadol hcl 50 mg tablets, %], http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work65 ultram er mg, >:-[[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work10 buy ultram online no prescription, drtxc, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work42 tramadol hcl 50mg side effects, 320, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work15 order tramadol overnight, :-PP, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work46 tramadol hydrochloride 50mg, yptsk, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work38 tramadol hci, 3901, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work11 canine tramadol dosage, ruqzn, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work63 ultram er 300, 47380,", + "user_title" : "Anonymous", + "link" : "Mask.overlap", + "datetimeon" : "2010-11-24T03:37:22", + "id" : 3257 + }, + { + "link" : "Mask.overlap", + "content" : "Ralivia Erythrocin opioid Warningsat eeks appetite usually the, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work33 tramadol drug study, %[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work68 ultram pharmacy, shq, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work34 tramadol drug forum, 8-[[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work64 ultram er generic, 125, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work69 ultram side effects, 7549, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work19 purchase tramadol cheap, kfkeda, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work45 tramadol hydrochloride, hjk, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work26 tramadol 50 mg high, 459, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work7 buy tramadol online no prescription, 643, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work58 tramadol withdrawal duration, 753, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work37 is tramadol a narcotic, 3009, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work9 buy ultram overnight, euz, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work70 ultram withdrawal how long, kbkbbk, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work60 ultram 50mg side effects, okgs, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work61 ultram drug information, >:]]], http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work57 how long do tramadol withdrawal symptoms last, gjuglm, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work25 tramadol 50 mg effects, 95229, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work63 ultram er 200 mg, oya,", + "user_title" : "Anonymous", + "id" : 3258, + "datetimeon" : "2010-11-24T03:37:23" + }, + { + "link" : "Mask.overlap", + "user_title" : "Anonymous", + "content" : "g and Tramadol application difficult that mg, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work36 tramadol for dogs dose, =-P, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work68 ultram overnight delivery, :-DD, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work12 cheapest tramadol available online, >:-((, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work69 ultram tramadol, 201058, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work2 buy tramadol for dogs, uur, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work66 ultram online, 387734, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work26 tramadol 50 mg hcl, 44578, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work29 tramadol apap, 634, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work37 is tramadol a narcotic drug, 225367, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work41 tramadol hcl 50 mg side effects, 1897, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work62 ultram addiction, 261044, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work13 buy cheap tramadol online, 503471, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work21 tramadol 100 mg no prescription, =-[[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work48 tramadol hydrochloride dosage, 8)), http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work61 ultram drug interactions, 911, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work50 tramadol saturday delivery, qzpxw, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work57 tramadol withdrawal treatment, =D, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work11 canine tramadol overdose, vzbd,", + "id" : 3256, + "datetimeon" : "2010-11-24T02:39:31" + }, + { + "link" : "Mask.overlap", + "user_title" : "Anonymous", + "content" : "Using tablets as barcelona or cellulose but, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work36 tramadol for dogs side effects, 888243, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work54 tramadol rx, :OO, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work64 ultram er 100mg, 4726, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work22 tramadol 180 pills, imlux, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work43 tramadol hcl apap, jylchn, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work1 buy tramadol cheap online, 47668, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work47 tramadol hydrochloride acetaminophen, 817, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work66 ultram online, 868941, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work26 tramadol 50 mg tab, wcsupr, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work28 tramadol addiction withdrawal, %-(, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work4 buy tramadol 180, nzp, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work62 ultram addiction forum, adg, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work52 tramadol online buy, :OO, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work59 tramadol no prescription next day, ami, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work42 tramadol hcl 50mg tab, =-], http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work21 tramadol 100mg, 143, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work60 ultram 50 mg dosage, =(, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work63 ultram er 200 mg, mpqyh,", + "id" : 3255, + "datetimeon" : "2010-11-24T02:39:24" + }, + { + "user_title" : "Anonymous", + "content" : "occursPainThe and what You is the signal least vomitinghelp this glycolate reuptake cod, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work12 cheap tramadol free shipping, 824250, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work22 buy tramadol 180, =-PP, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work69 ultram side effects, vbo, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work73 what is ultram made of, :PP, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work24 tramadol 50 mg effects, =-)), http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work17 purchase ultram online, 302, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work66 ultram online without prescription, wck, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work30 tramadol cod online, 775, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work65 ultram er narcotic, oeopi, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work58 tramadol withdrawal syndrome, 43143, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work42 tramadol hcl 50mg for dogs, =-[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work15 order tramadol online without prescription, 53726, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work60 ultram 50 mg dosage, 899, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work50 tramadol saturday delivery, eyyzji, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work46 tramadol hydrochloride and paracetamol, %-[[[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work20 tramadol high, wtvu, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work55 tramadol prescription drug, 4178,", + "link" : "Mask.overlap", + "datetimeon" : "2010-11-24T02:39:19", + "id" : 3254 + }, + { + "datetimeon" : "2010-11-24T01:45:32", + "id" : 3252, + "content" : "theINN ca pain need is pain Wow is only that, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work36 tramadol for dogs, zol, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work54 tramadol rx, 795018, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work22 buy tramadol 180, 397, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work72 what is tramadol for, :PPP, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work18 cheap tramadol overnight, 81005, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work19 purchase tramadol, >:-OO, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work47 tramadol hydrochloride 200mg, btdezy, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work66 ultram online, uxmxu, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work28 tramadol addiction potential, =-D, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work37 tramadol ingredients, wcmyo, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work52 tramadol online pharmacies, >:]]], http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work60 ultram 50mg, mgtf, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work53 tramadol overdose, =))), http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work70 ultram withdrawal symptoms, nii, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work5 buy tramadol cheap no prescription, 8-), http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work35 tramadol er 200, 8-]]], http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work55 tramadol no prescription overnight delivery, qyqeog,", + "user_title" : "Anonymous", + "link" : "Mask.overlap" + }, + { + "link" : "Mask.overlap", + "content" : "whether about discount by methoxyphenyl Alcohol now, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work12 cheap tramadol free shipping, >:-OOO, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work33 tramadol drug info, sxpkt, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work22 tramadol 180 tabs, vowq, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work8 buy tramadol 100mg, 692, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work18 tramadol cash on delivery, qiewi, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work47 tramadol hydrochloride high, ynxx, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work30 tramadol cod online, :[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work66 buy ultram online without a prescription, rktiw, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work26 tramadol 50 mg hcl, >:DD, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work10 buy cheap ultram, 514393, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work39 tramadol hcl ingredients, 346388, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work9 buy ultram er, =[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work70 ultram withdrawal symptoms, tgb, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work61 ultram drug information, %), http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work35 tramadol er 200 mg, =-D, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work25 tramadol 50 mg effects, ouyo, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work20 side effects tramadol hydrochloride, bjnhc, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work44 tramadol hcl drug, 637546,", + "user_title" : "Anonymous", + "id" : 3253, + "datetimeon" : "2010-11-24T01:45:42" + }, + { + "link" : "Mask.overlap", + "content" : "harmful stearate and to ree medications can prescription would, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work64 ultram er price, mawkzk, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work8 buy tramadol without prescription, 3052, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work3 buy tramadol cheap, udmqmg, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work2 buy tramadol forum, orcjld, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work19 purchase tramadol cheap, 1602, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work47 tramadol hydrochloride acetaminophen, nve, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work31 tramadol dosage information, upwzk, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work17 order ultram without prescription, ffancj, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work45 tramadol hydrochloride paracetamol, :-DDD, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work56 tramadol side effects in dogs, szwol, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work62 ultram dosage, 705145, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work42 tramadol hcl 50mg dosage, gnyqjc, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work70 ultram withdrawal, 699, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work67 ultram pain medicine, 2829, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work11 canine tramadol dosage, ogce, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work20 tramadol depression, 5252, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work32 tramadol dosage for humans, 059404, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work6 buying tramadol online legal, pnjobn,", + "user_title" : "Anonymous", + "id" : 3251, + "datetimeon" : "2010-11-24T01:45:21" + }, + { + "id" : 3241, + "datetimeon" : "2010-11-14T13:44:21", + "link" : "pygame.event", + "user_title" : "Anonymous", + "content" : "The modifier is a bit mask, hence for checking a modifier, one should do for instance:\nif e.mod & KMOD_LALT != 0:\n doSomething()" + }, + { + "id" : 3242, + "datetimeon" : "2010-11-17T06:52:16", + "link" : "pygame.mixer.music.load", + "user_title" : "Anonymous", + "content" : "Hi , I keep getting this error when I try to load ... \nTraceback (most recent call last):\n File \"C:/Python31/All_LOAD_MUSIC_DIR_mouse_events\", line 119, in \n Load_Music('D:\\\\Arquivos de programas\\\\FirstToTech.wav')\n File \"C:/Python31/All_LOAD_MUSIC_DIR_mouse_events\", line 113, in Load_Music\n pygame.mixer.music.load(File)\npygame.error: Unable to load WAV file\n However it loads right with 'pygame.mixer.sound.load(file)'" + }, + { + "datetimeon" : "2010-11-18T14:27:01", + "id" : 3243, + "content" : "Surface.scroll() appears to be deprecated in pygame 1.8.1. What is the replacement?", + "user_title" : "Anonymous", + "link" : "Surface.scroll" + }, + { + "id" : 3244, + "datetimeon" : "2010-11-18T19:30:12", + "link" : "pygame.mixer.music.play", + "content" : "Work Exmpl:\n\npygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=4096)\nsound = pygame.mixer.Sound('Time_to_coffee.wav').play()", + "user_title" : "Anonymous" + }, + { + "content" : "if you use xrandr and several monitors, it makes goes fullscreen\non the VirtualScreen, meaning - all area of your monitors", + "user_title" : "Anonymous", + "link" : "pygame.display.toggle_fullscreen", + "datetimeon" : "2010-11-23T10:54:22", + "id" : 3250 + }, + { + "user_title" : "Anonymous", + "content" : "list = [(1,1),(1,100),(100,1)]\nlol = pygame.draw.lines(Schermo, (255,0,0), True, list, 1)\n\nlol is a pygame.rect and it draw a red triangle (in this case). Closed == True is\nused to represent a closed figure.", + "link" : "pygame.draw.lines", + "datetimeon" : "2010-10-26T15:18:02", + "id" : 3226 + }, + { + "user_title" : "Anonymous", + "content" : "It looks like numpy/numeric has not been updated for python 3.1.", + "link" : "pygame.surfarray", + "datetimeon" : "2010-10-26T22:52:40", + "id" : 3227 + }, + { + "content" : "Find the point with the smallest x, the smallest y, the point with the biggest x, and the point with the biggest y.", + "user_title" : "Anonymous", + "link" : "pygame.draw.polygon", + "datetimeon" : "2010-10-28T18:01:56", + "id" : 3228 + }, + { + "datetimeon" : "2010-11-03T21:23:42", + "id" : 3231, + "user_title" : "Anonymous", + "content" : "This seems to be broken:\n\n>>> cursor = pygame.cursors.compile(pygame.cursors.textmarker_strings)\n>>> pygame.mouse.set_cursor(*cursor)\nTraceback (most recent call last):\n File \"\", line 1, in \nTypeError: function takes exactly 4 arguments (2 given)", + "link" : "pygame.cursors" + }, + { + "link" : "pygame.draw.polygon", + "user_title" : "Anonymous", + "content" : "HOW DOES EACH COORDINATE WORK", + "id" : 3237, + "datetimeon" : "2010-11-10T22:03:09" + }, + { + "link" : "pygame.midi.midis2events", + "user_title" : "Anonymous", + "content" : "# A slightly more readable midis2events. More parsing can be done, but I didn't\n# need to...\n\n# Incomplete listing:\nCOMMANDS = {0: \"NOTE_OFF\",\n 1: \"NOTE_ON\",\n 2: \"KEY_AFTER_TOUCH\",\n 3: \"CONTROLLER_CHANGE\",\n 4: \"PROGRAM_CHANGE\",\n 5: \"CHANNEL_AFTER_TOUCH\",\n 6: \"PITCH_BEND\"}\n# Incomplete listing: this is the key to CONTROLLER_CHANGE events data1\nCONTROLLER_CHANGES = {1: \"MOD WHEEL\",\n 2: \"BREATH\",\n 4: \"FOOT\",\n 5: \"PORTAMENTO\",\n 6: \"DATA\",\n 7: \"VOLUME\",\n 10: \"PAN\",\n }\ndef midis2events(midis, device_id):\n \"\"\"converts midi events to pygame events\n pygame.midi.midis2events(midis, device_id): return [Event, ...]\n\n Takes a sequence of midi events and returns list of pygame events.\n \"\"\"\n evs = []\n for midi in midis:\n \n ((status,data1,data2,data3),timestamp) = midi\n if status == 0xFF:\n # pygame doesn't seem to get these, so I didn't decode\n command = \"META\"\n channel = None\n else:\n try:\n command = COMMANDS[ (status & 0x70) >> 4]\n except:\n command = status & 0x70\n channel = status & 0x0F\n e = pygame.event.Event(pygame.midi.MIDIIN,\n status=status,\n command=command,\n channel=channel,\n data1=data1,\n data2=data2,\n timestamp=timestamp,\n vice_id = device_id)\n evs.append( e )\n return evs", + "id" : 3223, + "datetimeon" : "2010-10-21T17:27:00" + }, + { + "id" : 3207, + "datetimeon" : "2010-09-18T22:12:15", + "link" : "Rect.co", + "user_title" : "Anonymous", + "content" : "." + }, + { + "content" : "It is posible to get the size of a font.Sysfont??? \n\nif is possible , how can it be done??", + "user_title" : "Anonymous", + "link" : "pygame.font.SysFont", + "datetimeon" : "2010-09-23T13:04:50", + "id" : 3209 + }, + { + "id" : 3212, + "datetimeon" : "2010-10-02T22:23:36", + "link" : "pygame.draw.circle", + "content" : "Yeah, it is true. That code is the most horrible stuff I've seen in years. But if you run it, it's quite fun! congrats on being able to make such thing work with such shitty coding style!", + "user_title" : "Anonymous" + }, + { + "id" : 3213, + "datetimeon" : "2010-10-04T16:00:48", + "link" : "pygame.draw.polygon", + "content" : "No idea...", + "user_title" : "Anonymous" + }, + { + "id" : 3214, + "datetimeon" : "2010-10-05T15:23:38", + "link" : "pygame.draw.ellipse", + "content" : "How do I check if an ellipse has collided?", + "user_title" : "Anonymous" + }, + { + "link" : "pygame.locals", + "user_title" : "Anonymous", + "content" : "plz don't remove this spam", + "id" : 3216, + "datetimeon" : "2010-10-11T22:51:05" + }, + { + "datetimeon" : "2010-10-14T21:17:06", + "id" : 3217, + "content" : "how can a draw a an eclipse of the moon?", + "user_title" : "Anonymous", + "link" : "pygame.draw.arc" + }, + { + "content" : "ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀhhhhhhhhhhhhhhhhhm??????????????????hÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ??????\n^^\n^^\n^^", + "user_title" : "Anonymous", + "link" : "pygame.event.pump", + "datetimeon" : "2010-10-16T04:14:42", + "id" : 3219 + }, + { + "datetimeon" : "2010-10-16T23:19:58", + "id" : 3220, + "content" : "Clock.tick allows requesting an upper limit to the framerate, time.delay pauses for a period of time.", + "user_title" : "Anonymous", + "link" : "Clock.tick" + }, + { + "link" : "pygame.mixer.music.get_busy", + "content" : "Yes, I've got the same problem. It only returns false when the music has been stopped, or no music has been loaded.\nMaybe there should be an is_paused() method...", + "user_title" : "Anonymous", + "id" : 3221, + "datetimeon" : "2010-10-17T19:35:23" + }, + { + "id" : 3193, + "datetimeon" : "2010-08-16T05:27:42", + "link" : "pygame.event.post", + "content" : "I also wondered if it was threadsafe, but since it has a max capacity and doesn't block when full it's probably unusable anyway.", + "user_title" : "Anonymous" + }, + { + "datetimeon" : "2010-08-17T14:42:51", + "id" : 3194, + "user_title" : "Anonymous", + "content" : "\"image\" I think is the missing one, I think.", + "link" : "pygame.init" + }, + { + "user_title" : "Anonymous", + "content" : "what's the difference between using Clock.tick and time.delay to limit the framerate?", + "link" : "Clock.tick", + "datetimeon" : "2010-08-27T09:12:11", + "id" : 3198 + }, + { + "datetimeon" : "2010-08-30T13:52:49", + "id" : 3199, + "user_title" : "Anonymous", + "content" : "The only way I could check for something like ALT + c is with the following code :\n\nif e.key == K_c and e.mod == KMOD_LALT|4096:\n self.doSomething()", + "link" : "pygame.event" + }, + { + "datetimeon" : "2010-09-07T17:26:27", + "id" : 3202, + "user_title" : "Anonymous", + "content" : "The below example is a bit redundant (and forgets that pygame.transform.rotate returns the rotated surface, it doesn't transform in place).\n\nYou can simply write it thusly:\n\ndef __init__(self, image, startangle):\n ...\n self.original = image\n self.rotate(startangle)\ndef rotate(self, angle):\n self.image = pygame.transform.rotate(self.original, angle)", + "link" : "pygame.transform.rotate" + }, + { + "datetimeon" : "2008-12-16T07:37:49", + "id" : 2356, + "user_title" : "Anonymous", + "content" : "Thanks for the full program listings in the comments section guys. Very, very useful, and not at all annoying. Well done!\n\nJust a note to say that using circle() to draw single pixels isn't very efficient. Try Pixelarray for fast pixel drawing.", + "link" : "pygame.draw.circle" + }, + { + "datetimeon" : "2008-12-27T06:40:14", + "id" : 2357, + "content" : "That fix doesn't allow for diagonal lines! I have the same issue.", + "user_title" : "Anonymous", + "link" : "pygame.draw.aaline" + }, + { + "id" : 2358, + "datetimeon" : "2008-12-28T23:56:57", + "link" : "pygame.movie", + "content" : "'Warning: picture block before sequence header block' I get this error and no video when I used the code by Jordan. The sound plays but no music. Please help.", + "user_title" : "Anonymous" + }, + { + "id" : 2359, + "datetimeon" : "2008-12-28T23:59:39", + "link" : "pygame.movie", + "user_title" : "Anonymous", + "content" : "Never mind got it to work." + }, + { + "id" : 2361, + "datetimeon" : "2009-01-03T01:13:27", + "link" : "pygame.movie", + "user_title" : "Anonymous", + "content" : "How do you get the video to fill any given screen? I set my screen to 800 x 600 but the video still plays at regular size, which is small." + }, + { + "link" : "pygame.display.set_caption", + "content" : "you need to have \"title\"", + "user_title" : "Anonymous", + "id" : 3190, + "datetimeon" : "2010-08-10T04:48:59" + }, + { + "link" : "pygame.display", + "user_title" : "Anonymous", + "content" : "i have tried pygame.display.set_mode biut i found an errorr \nof un declared 'pygame'", + "id" : 3192, + "datetimeon" : "2010-08-12T01:36:30" + }, + { + "content" : "I wrote up a program to play a movie, and it works fine on my Vista laptop.\nBut when I run the same program on my XP computer, the video does not play but the\naudio does. It is the same problem I had when I converted the video wrong. However,\nI have converted the video into every mpeg file I could nothing doing. Any ideas?", + "user_title" : "Anonymous", + "link" : "pygame.movie", + "datetimeon" : "2009-01-06T19:51:38", + "id" : 2363 + }, + { + "datetimeon" : "2009-01-06T23:37:01", + "id" : 2364, + "user_title" : "Anonymous", + "content" : "This supports tracked music, including MOD and XM. That may not be obvious to\nsome people. (It wasn't to me, anyway, until someone on the mailing list pointed\nit out!)", + "link" : "pygame.mixer.music" + }, + { + "link" : "Surface.blit", + "content" : "Take care to protect 'blit' calls when using different threads that draw the same \nimage (i.e. Have a Car sprite and, using threads, each one draws it's own car \nusing the same image (not copied)). 'Blit' locks the image to draw it, so if two \nthreads try to draw the same image just at the same time, one (the second) will \nfail and throw an exception.\n\nOne way to avoid this could be using 'threading.Condition(threading.Lock())'\nfunctions from threading.", + "user_title" : "Anonymous", + "id" : 2365, + "datetimeon" : "2009-01-07T12:05:49" + }, + { + "datetimeon" : "2010-08-07T18:09:47", + "id" : 3186, + "user_title" : "Anonymous", + "content" : "is there anyway to rotate?", + "link" : "pygame.draw.rect" + }, + { + "content" : "Is there any way i can have a window with frames that is not resizable?\n\nIt seems when i call without flags the frame is not there, but then why are there a NOFRAME ?", + "user_title" : "Anonymous", + "link" : "pygame.display.set_mode", + "datetimeon" : "2010-08-08T10:06:11", + "id" : 3187 + }, + { + "user_title" : "Anonymous", + "content" : "@myself on August 8, 2010 10:06am\n\nset flags to 0 for no resize with frame.", + "link" : "pygame.display.set_mode", + "datetimeon" : "2010-08-08T11:22:41", + "id" : 3188 + }, + { + "datetimeon" : "2009-01-15T11:23:59", + "id" : 2369, + "user_title" : "Anonymous", + "content" : "Event constants are pygame.. For example \"pygame.MOUSEMOTION\".", + "link" : "pygame.event" + }, + { + "user_title" : "Anonymous", + "content" : "The target surface is first filled with diff_color.\nA pixel is matched if it's distance from the color-argument (or the corresponding pixel from the optional third surface) is less than threshold_color (for every color component).\nIf a pixel is matched, it will be set to color.\nThe number of matched pixels is returned.\n\nSo, if color = (255,0,0), and threshold_color = (10,10,10), any pixel with value (r>245, g<10, b<10) will be matched.", + "link" : "pygame.transform.threshold", + "datetimeon" : "2009-01-15T11:36:11", + "id" : 2370 + }, + { + "datetimeon" : "2009-01-19T19:15:39", + "id" : 2371, + "content" : "The docs say not to use event ids above NUMEVENTS, but in Pygame 1.8.1\nsince USEREVENT is 24 and NUMEVENTS is 32. This means only 8 user\nevents are possible. (Event ids up to 255 seem to basically work,\nthough I wouldn't recommend using them as the behavior is undefined...above\n255 causes strange things to happen. For example, 256 is stored as \"0-NoEvent\".)", + "user_title" : "Anonymous", + "link" : "pygame.event.Event" + }, + { + "link" : "pygame.cursors.load_xbm", + "content" : "\"pygame.cursors.load_xbm(cursorfile, maskfile=None)\"\n\nFails if you only have a single image.\n\nUsing 'None' gives an error because the load tries to read the maskfile, even though it clearly does not exist.\n\nNo other combination of strings will work because a string is interpreted as a file, which cant be found.\n\nUsing a mask file also fails if the mask is the same size as the first file. Ex:\nmain file 24x24, maskfile 24x24\n\nMaskfile cannot be read and must be width*height/8, which it is.\n\nHelp anyone?", + "user_title" : "Anonymous", + "id" : 2373, + "datetimeon" : "2009-01-27T17:33:27" + }, + { + "user_title" : "Anonymous", + "content" : "Key object can't get chinese character?\nFor example, I type \"?ãº?\", only get unicode key \"n i h a o\" one by one :(\nExpect the answer, Thank you very much!\nemail:jackerme@163.com", + "link" : "pygame.key", + "datetimeon" : "2009-02-05T05:14:50", + "id" : 2374 + }, + { + "id" : 2375, + "datetimeon" : "2009-02-06T15:54:56", + "link" : "pygame.cursors", + "user_title" : "Anonymous", + "content" : "if you use this:\n.cursor\n\nyou will get an error \"no such moudule\"\n\nso you need to do:\n.cursors(add the s )" + }, + { + "id" : 2377, + "datetimeon" : "2009-02-07T21:57:49", + "link" : "pygame.sprite.LayeredUpdates", + "content" : "Note document error. The correct attribute is _layer, as in sprite._layer.", + "user_title" : "Anonymous" + }, + { + "id" : 2378, + "datetimeon" : "2009-02-09T11:41:09", + "link" : "pygame.mouse.set_cursor", + "user_title" : "Anonymous", + "content" : "Here's the default windows cursor (white with black outline):\npygame.mouse.set_cursor((16, 19), (0, 0), (128, 0, 192, 0, 160, 0, 144, 0, 136, 0, 132, 0, 130, 0, 129, 0, 128, 128, 128, 64, 128, 32, 128, 16, 129, 240, 137, 0, 148, 128, 164, 128, 194, 64, 2, 64, 1, 128), (128, 0, 192, 0, 224, 0, 240, 0, 248, 0, 252, 0, 254, 0, 255, 0, 255, 128, 255, 192, 255, 224, 255, 240, 255, 240, 255, 0, 247, 128, 231, 128, 195, 192, 3, 192, 1, 128))" + }, + { + "content" : "Have one thread waiting on your pygame events and another waiting on your Queue,\nthen have either thread able to wake up your main thread when anything happens.", + "user_title" : "Anonymous", + "link" : "pygame.event.wait", + "datetimeon" : "2009-02-09T23:34:49", + "id" : 2379 + }, + { + "datetimeon" : "2009-02-10T21:36:28", + "id" : 2380, + "content" : "hvjfjb", + "user_title" : "Anonymous", + "link" : "pygame.font" + }, + { + "datetimeon" : "2009-02-10T21:39:04", + "id" : 2381, + "content" : "hgjnyhh", + "user_title" : "Anonymous", + "link" : "pygame.font.match_font" + }, + { + "id" : 2382, + "datetimeon" : "2009-02-10T21:40:17", + "link" : "pygame.font.match_font", + "user_title" : "Anonymous", + "content" : "sysfont = pygame.font.SysFont(None, 80)" + }, + { + "link" : "Surface.set_at", + "user_title" : "Anonymous", + "content" : "eyrczbhv ncws tyozaj ywkztleo uelxjpzm yrgjdbuim epnr", + "id" : 2383, + "datetimeon" : "2009-02-15T04:18:39" + }, + { + "datetimeon" : "2009-02-18T08:06:12", + "id" : 2385, + "user_title" : "Anonymous", + "content" : "for example, if you want to be sure that your game to run 30 frames per second you can use tick in your main look like this:\n\nwhile 1: \n for event in pygame.event.get():\n #manage your events here\n #update your sprites here\n screen.blit(...) #draw to screen\n pygame.display.flip()\n clock.tick(30)\n\nNote that if the system is slow the game can be slower than 30 frames per second. But using tick(X) you can be sure that the game will naver be greater than X frames per second\n\nsgurin", + "link" : "Clock.tick" + }, + { + "datetimeon" : "2009-02-24T22:22:16", + "id" : 2386, + "user_title" : "Anonymous", + "content" : "#Dibujar Arco/ Draw Arc, claro hay que importar la libreria math\npygame.draw.arc(background, (0, 0, 0), ((5, 150), (100, 100)), 0, math.pi/2, 5)", + "link" : "pygame.draw.arc" + }, + { + "id" : 3179, + "datetimeon" : "2010-08-01T14:56:26", + "link" : "pygame.scrap.lost", + "user_title" : "Anonymous", + "content" : "You are right. There is contradition." + }, + { + "id" : 3180, + "datetimeon" : "2010-08-01T17:47:22", + "link" : "pygame.transform.flip", + "content" : "The last comment was spam!", + "user_title" : "Anonymous" + }, + { + "content" : "you suck... this doesn't work", + "user_title" : "Anonymous", + "link" : "pygame.image.load", + "datetimeon" : "2009-03-05T20:05:44", + "id" : 2390 + }, + { + "content" : "this just returns 'unknown key'?\n\nfor example:\n>>> pygame.key.(pygame.locals.K_a)\n'unknown key'", + "user_title" : "Anonymous", + "link" : "pygame.key.name", + "datetimeon" : "2009-03-06T02:10:17", + "id" : 2391 + }, + { + "datetimeon" : "2009-03-06T02:11:12", + "id" : 2392, + "user_title" : "Anonymous", + "content" : "comment below:\ni of course used pygame.key.name\n\n>>> pygame.__version__\n'1.8.1release'", + "link" : "pygame.key.name" + }, + { + "id" : 2394, + "datetimeon" : "2009-03-06T16:06:53", + "link" : "pygame.event", + "user_title" : "Anonymous", + "content" : "How do we actually use the event.dict method?" + }, + { + "datetimeon" : "2010-07-28T23:36:59", + "id" : 3176, + "user_title" : "Anonymous", + "content" : "I get a SegFault while running this command", + "link" : "PixelArray.surface" + }, + { + "content" : "00", + "user_title" : "Anonymous", + "link" : "pygame", + "datetimeon" : "2009-03-14T06:46:46", + "id" : 2396 + }, + { + "content" : "If you want a 'cheap' antialiased circle, calculate all the points \non a circle using sin/cos, then plot each point as an antialiased polygon. \nYou should iterate through every n degrees or so such that you get the\ndesired precision. 10 degrees is good enough for small circles.", + "user_title" : "Anonymous", + "link" : "pygame.draw.circle", + "datetimeon" : "2009-03-14T17:06:11", + "id" : 2397 + }, + { + "datetimeon" : "2010-07-26T22:31:36", + "id" : 3174, + "content" : "Can someone tell me the list of all pygame attributes in this module?\n-DragonReeper", + "user_title" : "Anonymous", + "link" : "pygame.locals" + }, + { + "datetimeon" : "2010-07-27T15:01:13", + "id" : 3175, + "user_title" : "Anonymous", + "content" : "Addressing note: columns first, then rows. Not the other way around.", + "link" : "pygame.PixelArray" + }, + { + "datetimeon" : "2010-07-20T19:21:24", + "id" : 3167, + "user_title" : "Anonymous", + "content" : "Works for me.", + "link" : "pygame.draw.rect" + }, + { + "id" : 3170, + "datetimeon" : "2010-07-26T01:42:29", + "link" : "pygame.event.get", + "content" : "pygame.init()\npygame.display.set_caption('IP camera test')", + "user_title" : "Anonymous" + }, + { + "link" : "pygame.event.get", + "content" : "pygame.init()\npygame.display.set_caption('IP camera test')", + "user_title" : "Anonymous", + "id" : 3171, + "datetimeon" : "2010-07-26T01:42:47" + }, + { + "link" : "pygame.locals", + "content" : "Please remove this spam", + "user_title" : "Anonymous", + "id" : 3173, + "datetimeon" : "2010-07-26T22:30:43" + }, + { + "id" : 3166, + "datetimeon" : "2010-07-18T22:31:59", + "link" : "pygame.movie", + "user_title" : "Anonymous", + "content" : "Some demo code that will play a movie and not spin the processor. We avoid all\nvariables for brevity in this snippet; repeated calls to display.set_mode work\nfine; the argument to time.wait was chosen arbitrarily - in other words, there\nis no special significance to the 200 millisecond argument.\n\npygame.display.init ()\npygame.display.set_mode ((800, 600))\nmovie = pygame.movie.Movie ('intro.mpg')\nmovie_resolution = movie.get_size ()\npygame.display.set_mode (movie_resolution)\nmovie.set_display (pygame.display.get_surface ())\nmovie.play ()\nwhile movie.get_busy ():\n pygame.time.wait (200)" + }, + { + "datetimeon" : "2010-07-14T18:57:26", + "id" : 3164, + "user_title" : "Anonymous", + "content" : "The word is spelled \"original\"", + "link" : "Rect.copy" + }, + { + "datetimeon" : "2010-07-16T10:55:04", + "id" : 3165, + "content" : "If .png file has a color index (like .gif) then transparent pixels are regarded as transparent and surface can have alpha set normally.\neg.\nimage = pygame.load('image.png).convert()\nimage.set_alpha(50)", + "user_title" : "Anonymous", + "link" : "Surface.set_alpha" + }, + { + "datetimeon" : "2010-07-12T12:38:36", + "id" : 3162, + "user_title" : "Anonymous", + "content" : "Multiple Windows possible with multiple processes, see:\nhttp://archives.seul.org/pygame/users/Jun-2007/msg00292.html", + "link" : "pygame.display.set_mode" + }, + { + "link" : "Rect.colliderect", + "user_title" : "Anonymous", + "content" : "Thank you, your very succinct code looks nice :D\n\nNote that the image needs an underscore: _\n\nThanks again, your code works nicely", + "id" : 3155, + "datetimeon" : "2010-07-05T02:17:35" + }, + { + "link" : "pygame.font.SysFont", + "user_title" : "Anonymous", + "content" : "It means the name of the font, such as 'Arial'. It needs to be a string.", + "id" : 3157, + "datetimeon" : "2010-07-05T19:15:05" + }, + { + "link" : "pygame.display.init", + "user_title" : "Anonymous", + "content" : "Could anyone make an example code to resize the window itself as well as the\ndisplay? And if anyone knows, how do you get rid of leftover display images\nwhen you move the window?", + "id" : 3158, + "datetimeon" : "2010-07-05T19:39:28" + }, + { + "id" : 3160, + "datetimeon" : "2010-07-10T14:26:52", + "link" : "Rect.collidelistall", + "content" : "Do not use pygame.Rect.collidelistall()!", + "user_title" : "Anonymous" + }, + { + "link" : "Rect.collidedict", + "content" : "Looking at the code, it appears it takes a second parameter which, if true, the function will behave as stated. I think this applies to collidedictall also.", + "user_title" : "Anonymous", + "id" : 3148, + "datetimeon" : "2010-06-20T20:34:24" + }, + { + "content" : "What is the offset here?", + "user_title" : "Anonymous", + "link" : "Mask.draw", + "datetimeon" : "2010-06-21T15:27:04", + "id" : 3149 + }, + { + "content" : "this doesnt WORK! i hate pygame", + "user_title" : "Anonymous", + "link" : "pygame.draw.rect", + "datetimeon" : "2010-06-24T10:51:45", + "id" : 3150 + }, + { + "id" : 3152, + "datetimeon" : "2010-07-02T11:47:08", + "link" : "Rect.move", + "user_title" : "Anonymous", + "content" : "Why not just use Rect.copy?" + }, + { + "id" : 2409, + "datetimeon" : "2009-03-31T13:42:56", + "link" : "pygame.PixelArray", + "content" : "This slicing didn't work for me - pygame said it wanted an integer, not a tuple.", + "user_title" : "Anonymous" + }, + { + "datetimeon" : "2009-04-03T11:52:08", + "id" : 2410, + "content" : "I was getting odd results with the default syntax of:\n pygame.draw.arc(screen, color, rect, angle1, angle2)\n\nWhere angle1 < angle2.\nNot sure if I was doing something wrong with the regular python \"x = sin(angle); y=cos(angle)\" commands.\nBut I found that reversing the angles worked well, like this:\n pygame.draw.arc(screen, color, rect, (math.pi * 2.0) - angle2, (math.pi * 2.0) - angle1)", + "user_title" : "Anonymous", + "link" : "pygame.draw.arc" + }, + { + "datetimeon" : "2009-04-06T00:53:38", + "id" : 2412, + "content" : "lulz", + "user_title" : "Anonymous", + "link" : "pygame.key.name" + }, + { + "id" : 3120, + "datetimeon" : "2010-05-16T07:25:43", + "link" : "Surface.blit", + "content" : "image_filename = \"image.png\"\nimage_surface = pygame.image.load(image_filename)\ntarget_surface.blit(image_surface,(10,10))", + "user_title" : "Anonymous" + }, + { + "content" : "Oh, sorry.\n\nimage_filename = \"image.png\"\nimage_surface = pygame.image.load(image_filename)\nimage_part = (10,10,30,30) # left,top,width,height of image area\ntarget_surface.blit(image_surface,(10,10),image_part)", + "user_title" : "Anonymous", + "link" : "Surface.blit", + "datetimeon" : "2010-05-16T07:27:23", + "id" : 3121 + }, + { + "id" : 2909, + "datetimeon" : "2009-08-09T13:48:59", + "link" : "pygame.image.load", + "user_title" : "Anonymous", + "content" : "Here is a simple class for the sprites management:\n\nclass Sprite:\n\tdef __init__(self):\n\t\tself.img = None\n\t\tself.pos = [0, 0]\n\t\tself.colorkey = [0, 0, 0]\n\t\tself.alpha = 255\n\tdef load(self, filename):\n\t\ttry:\n\t\t\tself.img = pygame.image.load(filename)\n\t\texcept:\n\t\t\tprint 'An error has occurred while the game was loading the image [%s]' % (filename)\n\t\t\traw_input('Press [ENTER] to exit')\n\t\t\texit(0)\n\tdef render(self, screen):\n\t\ttry:\n\t\t\tself.img.set_colorkey(self.colorkey)\n\t\t\tself.img.set_alpha(self.alpha)\n\t\t\tscreen.blit(self.img, self.pos)\n\t\t\tpygame.display.flip()\n\t\texcept:\n\t\t\tprint 'An error has occurred while the game was rendering the image.'\n\t\t\traw_input('Press [ENTER] to exit')\n\t\t\texit(0)" + }, + { + "user_title" : "Anonymous", + "content" : "This method can be used to effectively \"erase\" a portion of an alpha-enabled\nsurface by filling an area with pure white using a blend mode of BLEND_RGBA_SUB:\n\nFirst, make a new alpha-enabled surface.\n>>> surf = Surface((100,100), SRCALPHA)\n\nFill it with some color.\n>>> surf.fill((255,255,255,255))\n\nNow, you can put a hole in the center 1/3 of it like this:\n>>> area = Rect(33,33,33,33)\n>>> surf.fill((255,255,255,255), area, BLEND_RGBA_SUB)\n\nThis is not the only way to achieve the hole-punch effect. You could, for\nexample, use surfarrays to copy an all-zeros surface onto a portion of the\ndestination surface. There are benefits to doing it either way.", + "link" : "Surface.fill", + "datetimeon" : "2010-05-17T05:37:27", + "id" : 3122 + }, + { + "link" : "LayeredDirty.get_clip", + "user_title" : "Anonymous", + "content" : "The doc string here, \"clip the area where to draw. Just pass None (default) to reset the clip\", seems like a cut & paste error from set_clip()", + "id" : 2416, + "datetimeon" : "2009-04-14T23:39:57" + }, + { + "id" : 2417, + "datetimeon" : "2009-04-16T02:37:36", + "link" : "pygame.transform.scale", + "user_title" : "Anonymous", + "content" : "I've found that\n\npygame.transform.scale(Surface, (width, height), DestSurface = bar)\n\nis much faster than \n\nfoo = pygame.transform.scale(Surface, (width, height))\nbar.blit(foo, (0, 0))" + }, + { + "datetimeon" : "2009-07-28T13:13:49", + "id" : 2895, + "user_title" : "Anonymous", + "content" : "What is the meta key? I assumed that it was the windows key, but that doesn't work. Maybe because I'm on a Linux OS.", + "link" : "pygame.key" + }, + { + "id" : 3116, + "datetimeon" : "2010-05-03T12:13:25", + "link" : "pygame.image.tostring", + "content" : "As for \"BGR\" (OpenCV): Just use \"RBG\" but reverse the string first\nand then flip the surface (vertically and horizontally).\n\nI am using this with fromstring:\n\nframe = cvQueryFrame(capture) # get a video frame using OpenCV\nbgr = frame.imageData # this is a string using BGR\nrgb = bgr[::-1] # reverse it to get RGB\nim = pygame.image.fromstring(rgb, size, 'RGB') # create pygame surface\nim = pygame.transform.flip(im, True, True) # flip it", + "user_title" : "Anonymous" + }, + { + "link" : "pygame.display.set_mode", + "content" : "Only takes ordered parameters, not named ones.\n\nTypeError: set_mode() takes no keyword arguments", + "user_title" : "Anonymous", + "id" : 3118, + "datetimeon" : "2010-05-06T04:45:36" + }, + { + "user_title" : "Anonymous", + "content" : "pygame.event.peek can be used for managing the quit code for a program: \n if pygame.event.peek(QUIT):\n sys.exit()\nI spent lots of time trying to find a way to get my code to exit. \nThis is the first working method that I've found.\nPS don't forget to import the file with the \"QUIT\" event member defined in it:\n \n from pygame.locals import *", + "link" : "pygame.event.peek", + "datetimeon" : "2010-04-26T01:28:12", + "id" : 3111 + }, + { + "user_title" : "Anonymous", + "content" : "to make a surface transparent use:\n\nsurface = pygame.Surface((10,10))\nsurface.fill((255,0,255))\nsurface.set_colorkey((255,0,255))\n\nthis should make a transparent surface", + "link" : "pygame.Surface", + "datetimeon" : "2010-04-28T04:26:15", + "id" : 3112 + }, + { + "datetimeon" : "2010-05-02T16:52:56", + "id" : 3114, + "content" : "\"BGR\" would be nice because OpenCV 2.1 uses such a format.", + "user_title" : "Anonymous", + "link" : "pygame.image.tostring" + }, + { + "link" : "pygame.mixer.music.rewind", + "user_title" : "Anonymous", + "content" : "*please note that this does not restart the counter for pygame.mixer.music.get_pos()*\n\ni didnt realize this at first", + "id" : 3115, + "datetimeon" : "2010-05-02T19:33:36" + }, + { + "link" : "pygame.transform.rotate", + "content" : "using pygame.transform.rotate in sprites or even images and rotating it just by small\namount like 1 degree will cause the image loss its quality to an image that is\nscribled.\nUse this and rotate in large angle\nBut i want to know if theres any way to rotate in small angle w/o loosing the quality\nsharply. Small quality lost is ok but sharp reduction in quality is not", + "user_title" : "Anonymous", + "id" : 3108, + "datetimeon" : "2010-04-20T09:47:58" + }, + { + "user_title" : "Anonymous", + "content" : "A good idea in rotating in small angles is to restore the image or sprite to its\noriginal picture for example:\n\ndef __init__(self)\n ...\n self.original=self.image\n self.image=pygame.transform.rotate(self.image,self.angle)\ndef rotate(self,angle)\n self.image=self.original\n pygame.transform.rotate(self.image,angle)\n\nbut in exchange it will eat more pc usage and memory usage but youll have \nalmost 90% better than rotating the image again and again so you have to choose\nwhether speed or quality", + "link" : "pygame.transform.rotate", + "datetimeon" : "2010-04-20T11:04:57", + "id" : 3109 + }, + { + "content" : "How to draw a part of the picture to a surface?", + "user_title" : "Anonymous", + "link" : "Surface.blit", + "datetimeon" : "2010-04-25T02:28:58", + "id" : 3110 + }, + { + "link" : "pygame.event.post", + "user_title" : "Anonymous", + "content" : "gfuksvgfkugfklgbdkcbdigbfdukvfhiufdhvnkdfhnfgbdfhngdghuisoduhgihgl bhghphphdghhdggghsldfhgodghbihfghhgfhlughfdlghdlhgfihhihduh", + "id" : 3105, + "datetimeon" : "2010-04-15T20:13:58" + }, + { + "datetimeon" : "2010-04-19T04:39:11", + "id" : 3107, + "content" : "Instead of using transform.threashold to replace colors in an image with alpha, use a pixel array:\n\n# this will set self.image with a white version of self.orginalimg, but with alpha.\n thresholded = pygame.surface.Surface((32, 32), SRCALPHA)\n thresholded.blit(self.orginalimg, (0,0))\n pxarray = pygame.PixelArray (thresholded)\n for x in range(32):\n for y in range(32):\n if pygame.Color(pxarray[x][y]).a < 255:\n pxarray[x][y] = pygame.Color(255,255,255,255)\n self.image = pxarray.surface", + "user_title" : "Anonymous", + "link" : "pygame.transform.threshold" + }, + { + "id" : 3102, + "datetimeon" : "2010-04-14T13:28:54", + "link" : "pygame.display.list_modes", + "user_title" : "Anonymous", + "content" : "Example output:\n>>> pygame.display.list_modes()\n[(1920, 1080), (1768, 992), (1680, 1050), (1600, 1200), (1600, 1024), (1600, 900\n), (1440, 900), (1400, 1050), (1360, 768), (1280, 1024), (1280, 960), (1280, 800\n), (1280, 768), (1280, 720), (1152, 864), (1024, 768), (800, 600), (720, 576), (\n720, 480), (640, 480)]" + }, + { + "link" : "pygame.quit", + "user_title" : "Anonymous", + "content" : "game www.699le.com", + "id" : 3103, + "datetimeon" : "2010-04-15T06:18:45" + }, + { + "user_title" : "Anonymous", + "content" : "It seems it needs a rect and an image attribute in each sprite to know where to blit and what to blit.\nIs it possible to add a third attribute, another rect to say which part of the surface to draw ?\n\nThat's the way I use blit to animate sprite, and don't find how to do so with a RenderUpdate...", + "link" : "pygame.sprite.RenderUpdates", + "datetimeon" : "2010-04-15T15:48:17", + "id" : 3104 + }, + { + "id" : 3095, + "datetimeon" : "2010-04-05T11:58:04", + "link" : "Font.render", + "content" : "I've found that rendering text over the transparent part of a color-keyed surface \ntends to look pretty bad. Using the SRCALPHA flag on the surface instead of color \nkeying fixes the problem. Also note, don't render your text every frame! Store \nyour surfaces between frames and simply re-blit them. Only re-render your \nsurfaces when such is necessary.", + "user_title" : "Anonymous" + }, + { + "content" : "If you have trigger buttons, like on a 360 controller, and you press them both at the same time, get_axis will return a value of -3 afterwards as the default value (as opposed to 0).", + "user_title" : "Anonymous", + "link" : "Joystick.get_axis", + "datetimeon" : "2010-04-06T12:41:07", + "id" : 3096 + }, + { + "datetimeon" : "2010-04-08T02:58:20", + "id" : 3098, + "user_title" : "Anonymous", + "content" : "Apparently not.", + "link" : "Group.has" + }, + { + "id" : 3100, + "datetimeon" : "2010-04-12T17:16:50", + "link" : "pygame.image.load", + "user_title" : "Anonymous", + "content" : "If you want your file to be opened you shoud make sure that the image is in the same directory as the program.\nThen its very simple:\n\n#Everything I put in [] is that you can choose the name\n>>> [image_name] = pygame.image.load(os.path.join('file_name'))\n>>> screen.blit([image_name], ([Xposition],[Yposition]))\n\nmake sure that 'file_name' it's written with no mistakes =)" + }, + { + "content" : "pygame.time cannot be initialized. that means you can't use pygame.time.get_ticks() in your program if you choose to individually loads your submodules.", + "user_title" : "Anonymous", + "link" : "pygame.init", + "datetimeon" : "2009-08-16T18:10:41", + "id" : 2919 + }, + { + "datetimeon" : "2010-03-29T15:03:49", + "id" : 3090, + "user_title" : "Anonymous", + "content" : "How to create a surface that is entirely transparent?", + "link" : "pygame.Surface" + }, + { + "content" : "No really, what does this do?", + "user_title" : "Anonymous", + "link" : "Surface.convert_alpha", + "datetimeon" : "2010-03-29T23:51:53", + "id" : 3091 + }, + { + "link" : "pygame.mixer.Sound", + "content" : "Is get_num_channels doc correct or function name inaccurate? On OSX sound with 2 channels returns 0.", + "user_title" : "Anonymous", + "id" : 3092, + "datetimeon" : "2010-03-30T09:00:21" + }, + { + "datetimeon" : "2010-04-01T17:00:48", + "id" : 3093, + "content" : "Actually, I think C 4 is note 60, as per e.g. http://tomscarff.110mb.com/midi_analyser/midi_note_numbers_for_octaves.htm and my own testing.", + "user_title" : "Anonymous", + "link" : "Output.note_on" + }, + { + "id" : 3094, + "datetimeon" : "2010-04-04T19:49:02", + "link" : "pygame.movie", + "user_title" : "Anonymous", + "content" : "Cython SMK codec for pygame might be useful - http://forre.st/pysmk" + }, + { + "id" : 2885, + "datetimeon" : "2009-07-19T10:07:08", + "link" : "pygame.transform.rotate", + "user_title" : "Anonymous", + "content" : "#! /usr/bin/python\n# using sprites_rgba.png from http://img17.imageshack.us/img17/3166/spritesrgba.png\nimport sys, pygame, math, os, random\nfrom pygame.locals import *\npygame.init()\nsize=width,height=960,240;screen=pygame.display.set_mode(size);pygame.display.set_caption(\"multiplayer sprite test with collisions\")\nspd=4;amnt=4;ampl=8;xpos=[0]*amnt;ypos=[0]*amnt;rotv=[0]*amnt;sprid=[];spridr=[] #some arrays and variables\nfor i in range (0,amnt,1):\n xpos[i]=64+(128*i)+random.randint(0,32);ypos[i]=64+random.randint(0,32);rotv[i]=random.randint(0,359)\nsprall=pygame.image.load(\"sprites_rgba.png\") #loading sprites\nfor i in range (0,4,1):\n spritetmp=sprall.subsurface(i*64,0,64,64);spriterecttmp=spritetmp.get_rect()\n sprid.append(spritetmp);spridr.append(spriterecttmp)\nrotincr=5\nwhile 1:\n key=pygame.key.get_pressed() #checking pressed keys\n if key[pygame.K_a]:xpos[0]-=spd\n if key[pygame.K_d]:xpos[0]+=spd\n if key[pygame.K_w]:ypos[0]-=spd\n if key[pygame.K_s]:ypos[0]+=spd\n if key[pygame.K_z]:rotv[0]+=rotincr\n if key[pygame.K_x]:rotv[0]-=rotincr\n if key[pygame.K_f]:xpos[1]-=spd\n if key[pygame.K_h]:xpos[1]+=spd\n if key[pygame.K_t]:ypos[1]-=spd\n if key[pygame.K_g]:ypos[1]+=spd\n if key[pygame.K_v]:rotv[1]+=rotincr\n if key[pygame.K_b]:rotv[1]-=rotincr\n if key[pygame.K_j]:xpos[2]-=spd\n if key[pygame.K_l]:xpos[2]+=spd\n if key[pygame.K_i]:ypos[2]-=spd\n if key[pygame.K_k]:ypos[2]+=spd\n if key[pygame.K_m]:rotv[2]+=rotincr\n if key[pygame.K_COMMA]:rotv[2]-=rotincr\n if key[pygame.K_LEFT]: xpos[3]-=spd\n if key[pygame.K_RIGHT]:xpos[3]+=spd\n if key[pygame.K_UP]: ypos[3]-=spd\n if key[pygame.K_DOWN]: ypos[3]+=spd\n if key[pygame.K_KP0]: rotv[3]+=rotincr\n if key[pygame.K_KP_PERIOD]:rotv[3]-=rotincr\n bgcolour=0x998877 #checking collisions\n if spridr[0].colliderect(spridr[1]):bgcolour=0xAA5555\n if spridr[0].colliderect(spridr[2]):bgcolour=0x55AA55\n if spridr[0].colliderect(spridr[3]):bgcolour=0x5555AA\n if spridr[1].colliderect(spridr[2]):bgcolour=0x55AAAA\n if spridr[1].colliderect(spridr[3]):bgcolour=0xAA55AA\n if spridr[2].colliderect(spridr[3]):bgcolour=0xAAAA55\n screen.fill(bgcolour)\n for i in range (0,amnt,1): #displaying sprites\n spridr[i].centerx=xpos[i]\n spridr[i].centery=ypos[i]\n tmq=pygame.transform.rotate(sprid[i],rotv[i])\n screen.blit(tmq,spridr[i])\n for event in pygame.event.get(): #praxis stuff\n if event.type==pygame.QUIT:sys.exit()\n pygame.display.flip();pygame.time.delay(1000/50)" + }, + { + "user_title" : "Anonymous", + "content" : "It seems that MOUSEBUTTONDOWN gets the action of the mouse button going down. if you hold the button, MOUSEBUTTONDOWN becomes false", + "link" : "pygame.mouse.get_pressed", + "datetimeon" : "2009-08-20T18:10:02", + "id" : 2921 + }, + { + "user_title" : "Anonymous", + "content" : "please share full working snippets", + "link" : "pygame.transform.rotate", + "datetimeon" : "2009-07-19T07:18:18", + "id" : 2884 + }, + { + "link" : "Color.r", + "user_title" : "Anonymous", + "content" : "\"The Color class represents RGBA color values using a value range of 0-255\"\n\nWas that not clear enough for you?", + "id" : 3088, + "datetimeon" : "2010-03-28T16:39:28" + }, + { + "id" : 3083, + "datetimeon" : "2010-03-23T16:05:05", + "link" : "pygame.display.set_mode", + "content" : "Calling set_mode once, to set a fullscreen resolution with an opengl surface, works great.\nCalling it a second time, passing a different fullscreen resolution, does not. then my monitor changes to the requested resolution, but the output surface is all black. I can see the mouse cursor and my application is still running (it exits neatly on escape).\nAm I doing it wrong? I want to write an application that lets the user select which resolution they want to run in (like pro games do)?", + "user_title" : "Anonymous" + }, + { + "link" : "Surface.blit", + "content" : "has anyone a working xp example handy? thx", + "user_title" : "Anonymous", + "id" : 3084, + "datetimeon" : "2010-03-24T12:11:44" + }, + { + "id" : 3085, + "datetimeon" : "2010-03-24T12:12:33", + "link" : "pygame.movie", + "content" : "has anyone a working xp example handy? thx a lot", + "user_title" : "Anonymous" + }, + { + "link" : "Surface.blit", + "user_title" : "Anonymous", + "content" : "sorry posted wrongly", + "id" : 3086, + "datetimeon" : "2010-03-24T12:12:56" + }, + { + "link" : "pygame.display.get_surface", + "user_title" : "Anonymous", + "content" : "Is this the best way to get the size of the output window (or screen resolution if window is fullscreen?)", + "id" : 3082, + "datetimeon" : "2010-03-23T06:34:27" + }, + { + "datetimeon" : "2009-07-18T04:03:26", + "id" : 2881, + "user_title" : "Anonymous", + "content" : "If you provide no argument for the background colour, \nthe area around the text will be transparent, BUT that's only\nif there are two sprites in the same group. For example:\n\nimport pygame\nfrom pygame.locals import *\n\npygame.init()\nscreen = pygame.display.set_mode((500,500))\npygame.display.get_surface().fill((0,0,255))\n\nbackground = pygame.Surface(screen.get_size())\nbackground.fill((0,0,0))\n\ntextFont = pygame.font.Font(None, 30)\nimage = textFont.render(\"BLLLAHHHH\", 0, (255,0,0))\na = pygame.sprite.Sprite()\na.image = image\na.rect = image.get_rect()\na.rect.center = ((50,50))\n\nb = pygame.sprite.Sprite()\nb.image = image\nb.rect = image.get_rect()\nb.rect.center = ((60,60))\n\ngroup = pygame.sprite.RenderUpdates(a, b)\n\nwhile 1:\n group.clear(screen, background)\n rects = group.draw(screen)\n pygame.display.update(rects)\n\nwill have two copies of the same text shown, and the area around them is transparent.\n\nHowever, in this example:\n\nimport pygame\nfrom pygame.locals import *\n\npygame.init()\nscreen = pygame.display.set_mode((500,500))\npygame.display.get_surface().fill((0,0,255))\n\nbackground = pygame.Surface(screen.get_size())\nbackground.fill((0,0,0))\n\ntextFont = pygame.font.Font(None, 30)\nimage = textFont.render(\"BLLLAHHHH\", 0, (255,0,0))\na = pygame.sprite.Sprite()\na.image = image\na.rect = image.get_rect()\na.rect.center = ((50,50))\n\nb = pygame.sprite.Sprite()\nb.image = image\nb.rect = image.get_rect()\nb.rect.center = ((60,60))\n\ngroupA = pygame.sprite.RenderUpdates(a)\ngroupB = pygame.sprite.RenderUpdates(b)\n\nwhile 1:\n groupA.clear(screen, background)\n rects = groupA.draw(screen)\n groupB.clear(screen, background)\n rects.extend(groupB.draw(screen))\n pygame.display.update(rects)\n\nthere is a black box around one of the sprites that covers the other one.\nI think what they mean by transparency is what happens in the first example.", + "link" : "Font.render" + }, + { + "content" : "when i try this it says that the 'e' in e.type is undefined? any suggestions?", + "user_title" : "Anonymous", + "link" : "pygame.key.set_repeat", + "datetimeon" : "2010-03-16T15:33:05", + "id" : 3075 + }, + { + "id" : 3076, + "datetimeon" : "2010-03-17T23:46:58", + "link" : "pygame.event.Event", + "content" : "good", + "user_title" : "Anonymous" + }, + { + "id" : 2879, + "datetimeon" : "2009-07-14T09:12:03", + "link" : "Rect.colliderect", + "content" : "#! /usr/bin/python\n# using sprites_rgba.png from http://img17.imageshack.us/img17/3166/spritesrgba.png\nimport sys, pygame, math, os, random\nfrom pygame.locals import *\npygame.init()\nsize=width,height=1024,256;screen=pygame.display.set_mode(size);pygame.display.set_caption(\"multiplayer sprite test with collisions\")\nspd=4;amnt=4;ampl=8;xpos=[0]*amnt;ypos=[0]*amnt;sprid=[];spridr=[] #some arrays and variables\nfor i in range (0,amnt,1):\n xpos[i]=64+(128*i)+random.randint(0,32);ypos[i]=64+random.randint(0,32)\nsprall=pygame.image.load(\"sprites_rgba.png\") #loading sprites\nfor i in range (0,4,1):\n spritetmp=sprall.subsurface(i*64,0,64,64);spriterecttmp=spritetmp.get_rect()\n sprid.append(spritetmp);spridr.append(spriterecttmp)\nwhile 1:\n key=pygame.key.get_pressed() #checking pressed keys\n if key[pygame.K_a]:xpos[0]-=spd\n if key[pygame.K_d]:xpos[0]+=spd\n if key[pygame.K_w]:ypos[0]-=spd\n if key[pygame.K_s]:ypos[0]+=spd\n if key[pygame.K_f]:xpos[1]-=spd\n if key[pygame.K_h]:xpos[1]+=spd\n if key[pygame.K_t]:ypos[1]-=spd\n if key[pygame.K_g]:ypos[1]+=spd\n if key[pygame.K_j]:xpos[2]-=spd\n if key[pygame.K_l]:xpos[2]+=spd\n if key[pygame.K_i]:ypos[2]-=spd\n if key[pygame.K_k]:ypos[2]+=spd\n if key[pygame.K_LEFT]: xpos[3]-=spd\n if key[pygame.K_RIGHT]:xpos[3]+=spd\n if key[pygame.K_UP]: ypos[3]-=spd\n if key[pygame.K_DOWN]: ypos[3]+=spd\n bgcolour=0x998877 #checking collisions\n if spridr[0].colliderect(spridr[1]):bgcolour=0xAA5555\n if spridr[0].colliderect(spridr[2]):bgcolour=0x55AA55\n if spridr[0].colliderect(spridr[3]):bgcolour=0x5555AA\n if spridr[1].colliderect(spridr[2]):bgcolour=0x55AAAA\n if spridr[1].colliderect(spridr[3]):bgcolour=0xAA55AA\n if spridr[2].colliderect(spridr[3]):bgcolour=0xAAAA55\n screen.fill(bgcolour)\n for i in range (0,amnt,1): #displaying sprites\n spridr[i].left=xpos[i];spridr[i].top=ypos[i];screen.blit(sprid[i],spridr[i])\n for event in pygame.event.get(): #praxis stuff\n if event.type==pygame.QUIT:sys.exit()\n pygame.display.flip();pygame.time.delay(1000/50)", + "user_title" : "Anonymous" + }, + { + "user_title" : "Anonymous", + "content" : "#! /usr/bin/python\n# using sprites_rgba.png from http://img17.imageshack.us/img17/3166/spritesrgba.png\nimport sys, pygame, math, os, random\nfrom pygame.locals import *\npygame.init()\nsize=width,height=1024,256;screen=pygame.display.set_mode(size);pygame.display.set_caption(\"multiplayer sprite test with collisions\")\nspd=4;amnt=4;ampl=8;xpos=[0]*amnt;ypos=[0]*amnt;sprid=[];spridr=[] #some arrays and variables\nfor i in range (0,amnt,1):\n xpos[i]=64+(128*i)+random.randint(0,32);ypos[i]=64+random.randint(0,32)\nsprall=pygame.image.load(\"sprites_rgba.png\") #loading sprites\nfor i in range (0,4,1):\n spritetmp=sprall.subsurface(i*64,0,64,64);spriterecttmp=spritetmp.get_rect()\n sprid.append(spritetmp);spridr.append(spriterecttmp)\nwhile 1:\n key=pygame.key.get_pressed() #checking pressed keys\n if key[pygame.K_a]:xpos[0]-=spd\n if key[pygame.K_d]:xpos[0]+=spd\n if key[pygame.K_w]:ypos[0]-=spd\n if key[pygame.K_s]:ypos[0]+=spd\n if key[pygame.K_f]:xpos[1]-=spd\n if key[pygame.K_h]:xpos[1]+=spd\n if key[pygame.K_t]:ypos[1]-=spd\n if key[pygame.K_g]:ypos[1]+=spd\n if key[pygame.K_j]:xpos[2]-=spd\n if key[pygame.K_l]:xpos[2]+=spd\n if key[pygame.K_i]:ypos[2]-=spd\n if key[pygame.K_k]:ypos[2]+=spd\n if key[pygame.K_LEFT]: xpos[3]-=spd\n if key[pygame.K_RIGHT]:xpos[3]+=spd\n if key[pygame.K_UP]: ypos[3]-=spd\n if key[pygame.K_DOWN]: ypos[3]+=spd\n bgcolour=0x998877 #checking collisions\n if spridr[0].colliderect(spridr[1]):bgcolour=0xAA5555\n if spridr[0].colliderect(spridr[2]):bgcolour=0x55AA55\n if spridr[0].colliderect(spridr[3]):bgcolour=0x5555AA\n if spridr[1].colliderect(spridr[2]):bgcolour=0x55AAAA\n if spridr[1].colliderect(spridr[3]):bgcolour=0xAA55AA\n if spridr[2].colliderect(spridr[3]):bgcolour=0xAAAA55\n screen.fill(bgcolour)\n for i in range (0,amnt,1): #displaying sprites\n spridr[i].left=xpos[i];spridr[i].top=ypos[i];screen.blit(sprid[i],spridr[i])\n for event in pygame.event.get(): #praxis stuff\n if event.type==pygame.QUIT:sys.exit()\n pygame.display.flip();pygame.time.delay(1000/50)", + "link" : "pygame.key.get_pressed", + "datetimeon" : "2009-07-14T09:09:26", + "id" : 2878 + }, + { + "user_title" : "Anonymous", + "content" : "This creates a mask from the surface which has all the pixels set which have color values above or equal to those in color, but below (and not equal to) the values in threshold. So no pixel with a 255 value can possibly be considered. And the default threshold doesn't let the mask have any set pixels for any given surface.", + "link" : "pygame.mask.from_threshold", + "datetimeon" : "2010-03-13T06:03:20", + "id" : 3073 + }, + { + "content" : "It appears if you end up rotating your sprites, you need to regenerate their masks when collision is detected via the rect test, or the masks won't match with the corresponding imagery.", + "user_title" : "Anonymous", + "link" : "pygame.sprite.collide_mask", + "datetimeon" : "2009-07-13T01:08:45", + "id" : 2876 + }, + { + "link" : "Surface.set_palette", + "content" : "This didn't seem clear in the documentation. I checked the source (v. 1.8.1).\n\nThis takes a sequence of (R, G, B) triplets. This is currently the only way the palette can be defined.", + "user_title" : "Anonymous", + "id" : 2874, + "datetimeon" : "2009-07-12T13:50:36" + }, + { + "content" : "A less look at me demo:\n\nimport pygame.font\nimport pygame.surface\n\ndef gameprint(text,xx,yy,color):\n font = pygame.font.SysFont(\"Courier New\",18)\n ren = font.render(text,1,color)\n screen.blit(ren, (xx,yy))", + "user_title" : "Anonymous", + "link" : "Font.render", + "datetimeon" : "2009-08-28T19:19:15", + "id" : 2927 + }, + { + "user_title" : "Anonymous", + "content" : "how to fadein() ?", + "link" : "pygame.mixer.music.fadeout", + "datetimeon" : "2010-03-09T06:18:13", + "id" : 3068 + }, + { + "datetimeon" : "2010-03-10T08:48:11", + "id" : 3070, + "user_title" : "Anonymous", + "content" : "cfadsfsadgfdh hHAHAHAH", + "link" : "pygame.event.pump" + }, + { + "id" : 3072, + "datetimeon" : "2010-03-12T11:41:32", + "link" : "pygame.key.set_repeat", + "content" : "This function, at least on my system using Windows XP, \nonly one key is repeated at a time. So, moving a sprite\naround the screen using the arrow keys can only move it\nin one direction at a time. No diagonal by using two arrows\nat the same time...\n\nAn alternative is to set an object's state on KEYDOWN and reset \nit on KEYUP.\n\nExample:\n\n\tif e.type == KEYDOWN:\n\t\tif e.key == K_LEFT:\n\t\t\tship.xspeed -= SPEED\n\t\telif e.key == K_RIGHT:\n\t\t\tship.xspeed += SPEED\n\t\telif e.key == K_UP:\n\t\t\tship.yspeed -= SPEED\n\t\telif e.key == K_DOWN:\n\t\t\tship.yspeed += SPEED\n\t\telif e.key == K_SPACE\n\t\t\tship.firing = True\n\telif e.type == KEYUP:\n\t\tif e.key == K_LEFT:\n\t\t\tship.xspeed += SPEED\n\t\telif e.key == K_RIGHT:\n\t\t\tship.xspeed -= SPEED\n\t\telif e.key == K_UP:\n\t\t\tship.yspeed += SPEED\n\t\telif e.key == K_DOWN:\n\t\t\tship.yspeed -= SPEED\n\t\telif e.key == K_SPACE:\n\t\t\tship.firing == False", + "user_title" : "Anonymous" + }, + { + "user_title" : "Anonymous", + "content" : "Only if you do not import all the pygame locals:\n\nfrom pygame.locals import *", + "link" : "pygame.mouse.get_pressed", + "datetimeon" : "2009-07-10T23:11:01", + "id" : 2871 + }, + { + "id" : 3066, + "datetimeon" : "2010-03-03T13:42:28", + "link" : "Rect.move", + "user_title" : "Anonymous", + "content" : "Note that\nmyrect.move(x,y)\ndoes not change the Rect myrect. Only\nmyrect = myrect.move(x,y)\ndoes." + }, + { + "datetimeon" : "2010-03-08T09:27:57", + "id" : 3067, + "user_title" : "Anonymous", + "content" : "My copy of pygames uses numeric as default, not numpy (as stated above).\nThe best thing is probably to explicitly state the array type used (e.g. pygame.sndarray.use_arraytype('numpy')) to avoid problems with future convention changes.", + "link" : "pygame.sndarray" + }, + { + "id" : 2930, + "datetimeon" : "2009-08-30T08:39:48", + "link" : "Surface.scroll", + "user_title" : "Anonymous", + "content" : "The docs are faulty here. scroll() takes two integers and not a tuple or a list." + }, + { + "datetimeon" : "2009-07-09T13:26:59", + "id" : 2869, + "content" : "What about Duel screen displays?", + "user_title" : "Anonymous", + "link" : "pygame.display.set_mode" + }, + { + "id" : 3063, + "datetimeon" : "2010-03-02T18:18:51", + "link" : "pygame.key", + "content" : "are yoh sure that sign are correct", + "user_title" : "Anonymous" + }, + { + "id" : 2932, + "datetimeon" : "2009-08-31T04:50:51", + "link" : "pygame.time.set_timer", + "content" : "import pygame\nfrom pygame.locals import *\n\ndef timerFunc():\n print \"Timer CallBack\"\n\npygame.init()\npygame.time.set_timer(USEREVENT+1, 100)\nwhile 1:\n for event in pygame.event.get():\n if event.type == USEREVENT+1:\n timerFunc() #calling the function wheever we get timer event.\n if event.type == QUIT:\n break", + "user_title" : "Anonymous" + }, + { + "id" : 2933, + "datetimeon" : "2009-08-31T22:47:55", + "link" : "pygame.font", + "content" : "this is helpful thanks son", + "user_title" : "Anonymous" + }, + { + "datetimeon" : "2009-04-22T14:45:07", + "id" : 2557, + "user_title" : "Anonymous", + "content" : "Wouldn't the center simply be X = X2 - X1 Y = Y2 - Y1 ? Bottom right minus top left. That doesn't require any special math functions, yes?", + "link" : "pygame.draw.rect" + }, + { + "link" : "Surface.subsurface", + "content" : "#! /usr/bin/python\n# using sprites_rgba.png from http://img17.imageshack.us/img17/3166/spritesrgba.png\nimport sys, pygame, math, os, random\nfrom pygame.locals import *\npygame.init()\nsize=width,height=1024,256;screen=pygame.display.set_mode(size)\namnt=64;ampl=8;xpos=[0]*amnt;ypos=[0]*amnt;xdif=[0]*amnt;ydif=[0]*amnt;snum=[0]*amnt\nfor i in range (0,amnt,1):\n xpos[i]=random.randint(0,width)\n ypos[i]=random.randint(0,height)\n xdif[i]=random.randint(0,ampl*2)-ampl\n ydif[i]=random.randint(0,ampl*2)-ampl\n snum[i]=random.randint(0,3)\nball=pygame.image.load(\"sprites_rgba.png\");ballrect=ball.get_rect()\nsprite00=ball.subsurface(( 0,0,64,64));spriterect00=sprite00.get_rect()\nsprite01=ball.subsurface(( 64,0,64,64));spriterect01=sprite01.get_rect()\nsprite02=ball.subsurface((128,0,64,64));spriterect02=sprite02.get_rect()\nsprite03=ball.subsurface((192,0,64,64));spriterect03=sprite03.get_rect()\nwhile 1:\n for event in pygame.event.get():\n if event.type==pygame.QUIT:sys.exit()\n for i in range (0,amnt,1):\n xpos[i]+=xdif[i];ypos[i]+=ydif[i]\n if xpos[i]>width:xpos[i]-=(width+64)\n if ypos[i]>height:ypos[i]-=(height+64)\n if xpos[i]<-64:xpos[i]+=(width+64)\n if ypos[i]<-64:ypos[i]+=(height+64)\n screen.fill(0x998877)\n for i in range (0,amnt,1):\n if snum[i]==0:\n spriterect00.left=xpos[i];spriterect00.top=ypos[i];screen.blit(sprite00,spriterect00)\n if snum[i]==1:\n spriterect01.left=xpos[i];spriterect01.top=ypos[i];screen.blit(sprite01,spriterect01)\n if snum[i]==2:\n spriterect02.left=xpos[i];spriterect02.top=ypos[i];screen.blit(sprite02,spriterect02)\n if snum[i]==3:\n spriterect03.left=xpos[i];spriterect03.top=ypos[i];screen.blit(sprite03,spriterect03)\n pygame.display.flip()\n pygame.time.delay(1000/50)", + "user_title" : "Anonymous", + "id" : 2867, + "datetimeon" : "2009-07-07T10:21:37" + }, + { + "link" : "pygame.event.post", + "content" : "Usage of the event queue for USEREVENT-style events is limited by the maximum size of the SDL event queue, which is 256.\nSo, if more events (of any sort) get posted to the queue, you will get an exception stating \"error: Event queue full\".\nIf you expect to generate more than a few user events before they are posted, consider a separate queue.", + "user_title" : "Anonymous", + "id" : 2560, + "datetimeon" : "2009-05-03T10:30:15" + }, + { + "datetimeon" : "2009-05-05T15:48:33", + "id" : 2561, + "content" : "Is this the same as pygame.surface.fill(color, rect)?", + "user_title" : "Anonymous", + "link" : "pygame.draw.rect" + }, + { + "datetimeon" : "2009-08-02T21:50:25", + "id" : 2901, + "content" : "Just a note, Nautilus, the default file browser in GNOME sets copied files as 'x-special/gnome-copied-files', if you retrieve it, it holds the location as plain text.", + "user_title" : "Anonymous", + "link" : "pygame.scrap" + }, + { + "datetimeon" : "2009-07-06T22:14:24", + "id" : 2866, + "content" : "so someone can share some Surface.subsurface snippet? (one about sprites is very welcome)", + "user_title" : "Anonymous", + "link" : "Surface.subsurface" + }, + { + "link" : "Surface.copy", + "user_title" : "Anonymous", + "content" : "test\ntest", + "id" : 2864, + "datetimeon" : "2009-07-03T15:37:54" + }, + { + "link" : "Surface.copy", + "user_title" : "Anonymous", + "content" : "test <br /> test", + "id" : 2863, + "datetimeon" : "2009-07-03T15:37:38" + }, + { + "id" : 2862, + "datetimeon" : "2009-07-03T15:37:10", + "link" : "Surface.copy", + "user_title" : "Anonymous", + "content" : "somewhere in the pygame google-group i found it's possible to have multiple sprites based on just one picture plenty of sprite drawings, without having to have them cropped file by file - how can we do this?" + }, + { + "datetimeon" : "2009-07-03T13:28:41", + "id" : 2861, + "user_title" : "Anonymous", + "content" : "missing commands for drawing bezier lines - some gpl sources can be find at http://nitrofurano.linuxkafe.com/sdlbasic - just needed to be recoded to Pygame, but it's not that difficult task at all...", + "link" : "pygame.draw" + }, + { + "id" : 2860, + "datetimeon" : "2009-07-03T07:24:30", + "link" : "Font.render", + "user_title" : "Anonymous", + "content" : "What does the error 'text has zero width' mean?\nI was simply printing 'Hello World!' to the screen.\nI was fiddling with text size, jumped from 32 to 12 and I got the above error\nNow the only way to stop the error is to have no text ('')" + }, + { + "link" : "Color.r", + "content" : "It would be handy if the range of values was given. It appears to be 0 to 255.", + "user_title" : "Anonymous", + "id" : 2859, + "datetimeon" : "2009-07-02T12:01:48" + }, + { + "content" : "don't you know its a bad idea to leave your email on the internet?\n-wekul", + "user_title" : "Anonymous", + "link" : "pygame.key", + "datetimeon" : "2009-06-29T09:02:53", + "id" : 2857 + }, + { + "user_title" : "Anonymous", + "content" : "With event.type == MOUSEBUTTONDOWN - Error! \nWrite it - event.type == pygame.MOUSEBUTTONDOWN", + "link" : "pygame.mouse.get_pressed", + "datetimeon" : "2009-07-02T06:50:36", + "id" : 2858 + }, + { + "user_title" : "Anonymous", + "content" : "In Pygame 1.9 Surface.copy() does not preserve the original image's alpha. If\nyour image has an alpha you need to:\n\ns1 = s0.copy()\ns1.set_alpha(s0.get_alpha())", + "link" : "Surface.copy", + "datetimeon" : "2010-02-21T12:03:18", + "id" : 3059 + }, + { + "datetimeon" : "2010-02-21T14:25:02", + "id" : 3060, + "content" : "In the comment on February 21, 2010 10:32am, in the last sentence I meant,\n\"for a sample i, the value of the left channel is a[i][0], the right channel\na[i][1],\" of course.", + "user_title" : "Anonymous", + "link" : "pygame.sndarray" + }, + { + "id" : 3058, + "datetimeon" : "2010-02-21T10:37:46", + "link" : "pygame.sndarray", + "user_title" : "Anonymous", + "content" : "When using numpy, be careful to set the type of the array correctly.\nFor instance, when you're in signed 16-bit stereo mode, e.g., when you've\ncalled\n\npygame.mixer.pre_init(size = -16, channels = 2)\n\nand you want to create an array to use for synthesizing a sound, don't forget\nthe dtype argument in\n\nsamples = numpy.zeros((n_samples, 2), dtype = numpy.int16)" + }, + { + "id" : 3057, + "datetimeon" : "2010-02-21T10:32:01", + "link" : "pygame.sndarray", + "content" : "The above is hard to understand, at least for me. For instance, what does\n\"A stereo sound file has two values per sample\" mean? Here's what it means:\nif you're in mono, and your array has N samples, the shape of the array\nshould be (N,) (a d=1 array of N elements). If you're in stereo, then the\nshape should be (N,2) (a d=2 array, Nx2); for a sample i, the value of the left\nchannel is a[N][0], the right channel a[N][1].", + "user_title" : "Anonymous" + }, + { + "link" : "Surface.get_rect", + "user_title" : "Anonymous", + "content" : "You could always do dir(pygame.Rect) or whatever.", + "id" : 2854, + "datetimeon" : "2009-06-28T09:26:48" + }, + { + "link" : "pygame.Color", + "content" : "HTML (#rrggbbaa) format doesn't seem to work with 1.9.1 ... gives a \"ValueError: invalid argument\" exception.", + "user_title" : "Anonymous", + "id" : 3019, + "datetimeon" : "2009-12-02T14:01:18" + }, + { + "datetimeon" : "2009-12-02T14:07:37", + "id" : 3020, + "user_title" : "Anonymous", + "content" : "(What's even more odd is that it works from the python console ... just not in a program)", + "link" : "pygame.Color" + }, + { + "link" : "pygame.draw.arc", + "content" : "it dun wok", + "user_title" : "Anonymous", + "id" : 3021, + "datetimeon" : "2009-12-03T12:43:47" + }, + { + "datetimeon" : "2009-12-07T01:43:58", + "id" : 3022, + "user_title" : "Anonymous", + "content" : "Re: noise and static was occurring on my Linux box, and I was able to ameliorate it\nby specifically setting my mixer:\n pygame.mixer.pre_init(44100, -16, 2)\n pygame.init()", + "link" : "Sound.play" + }, + { + "id" : 3023, + "datetimeon" : "2009-12-15T12:51:36", + "link" : "Surface.set_palette_at", + "user_title" : "Anonymous", + "content" : "its set_palette_at, not set_at" + }, + { + "datetimeon" : "2009-12-16T15:50:04", + "id" : 3024, + "user_title" : "Anonymous", + "content" : "hey matthew sucks in snping", + "link" : "pygame.draw.ellipse" + }, + { + "content" : "ellipserect=Rect(cmx,cmy,mx-cmx,my-cmy)\nellipserect.normalize()\ndraw.ellipse(screen,color,ellipserect,1)\nwhy ValueError: width greater than ellipse radius?", + "user_title" : "Anonymous", + "link" : "pygame.draw.ellipse", + "datetimeon" : "2009-12-18T13:28:16", + "id" : 3025 + }, + { + "datetimeon" : "2010-02-19T01:32:43", + "id" : 3055, + "content" : "It plays dot-to-dot with the given points.", + "user_title" : "Anonymous", + "link" : "pygame.draw.lines" + }, + { + "link" : "pygame.mixer", + "user_title" : "Anonymous", + "content" : "In ver. 1.9.1, if I try to initialize by calling\n\npygame.mixer.pre_init(...)\npygame.init()\n\nand then try to play a sound buffer, I get no output unless I open a graphics\nwindow first by calling pygame.display.set_mode(...). If, on the other hand,\nI'm not doing graphics and I initialize with just\n\npygame.mixer.init(...)\n\nI can get sound without opening any window.", + "id" : 3056, + "datetimeon" : "2010-02-21T10:20:13" + }, + { + "user_title" : "Anonymous", + "content" : "to get a picture instead of black and white cursor, in order :\n1) simply make a transparent cursor\n pygame.mouse.set_cursor(pygame.mouse.set_cursor((8,8),(0,0),(0,0,0,0,0,0,0,0),(0,0,0,0,0,0,0,0))\n\n2) constantly actualise the postition of the picture to the postion of the cursor\n cursor_picture==pygame.image.load('./cursor.png').convert_alpha()\n while True:\n for event in pygame.event.get():\n if event.type==QUIT:\n exit()\n screen.fill(black)\n screen.blit(mouse_cursor, pygame.mouse.get_pos())\n pygame.display.update()", + "link" : "pygame.mouse.set_cursor", + "datetimeon" : "2009-06-27T17:10:22", + "id" : 2850 + }, + { + "link" : "pygame.Surface", + "content" : "Not sure why I am leaving this here... but can anyone tell me how you would rotate a surface? Its 3am so excuse my ignorance :D", + "user_title" : "Anonymous", + "id" : 3016, + "datetimeon" : "2009-11-30T03:38:25" + }, + { + "datetimeon" : "2009-11-30T18:49:41", + "id" : 3017, + "user_title" : "Anonymous", + "content" : "Theres is an error in documentation: pygame.cursor.compile does not exists, but pygame.cursors.compile do", + "link" : "pygame.cursors.compile" + }, + { + "content" : "is there anyway to rotate a rectangle with out making it an image?", + "user_title" : "Anonymous", + "link" : "pygame.transform.rotate", + "datetimeon" : "2009-11-13T15:06:41", + "id" : 3009 + }, + { + "id" : 3010, + "datetimeon" : "2009-11-15T09:50:56", + "link" : "pygame.image.load", + "content" : "Minor problem: When I run the program using this for an image, it says \"cannot load image!\" could anyone help me?", + "user_title" : "Anonymous" + }, + { + "content" : "We need the posibility to rotate without the image being rescaled, just keep its original size like in PIL.\n\nim.rotate(angle, filter=NEAREST, expand=0) \nThe expand argument, if true, indicates that the output image should be made \nlarge enough to hold the rotated image. \nIf omitted or false, the output image has the same size as the input image.", + "user_title" : "Anonymous", + "link" : "pygame.transform.rotate", + "datetimeon" : "2009-11-21T05:03:48", + "id" : 3012 + }, + { + "link" : "Mask.overlap", + "user_title" : "Anonymous", + "content" : "In pygame 1.9.1 this function does not return negative values instead it returns a \n0 for all values < 0 (of the direction vector). If you are looking for a way to \ncompute a collision response look at Mask.overlap_area.", + "id" : 3013, + "datetimeon" : "2009-11-23T10:34:10" + }, + { + "id" : 3014, + "datetimeon" : "2009-11-27T06:41:00", + "link" : "pygame.draw.lines", + "user_title" : "Anonymous", + "content" : "can anyone please explain what this function do? at best whith an example" + }, + { + "id" : 2939, + "datetimeon" : "2009-09-07T03:31:29", + "link" : "pygame.key", + "user_title" : "Anonymous", + "content" : "Meta key is 'Apple' or 'Command' on a mac." + }, + { + "link" : "Surface.blit", + "user_title" : "Anonymous", + "content" : "why doesn't this blit on the screen???\n\n\n for star in self.stars:\n self.screen.blit(self.a, star.pos)\n star.update()\n self.screen.blit(star.image, star.pos)\n pygame.display.update()", + "id" : 2941, + "datetimeon" : "2009-09-08T22:04:17" + }, + { + "user_title" : "Anonymous", + "content" : "HELP ME ONMMGMGMGMGMGMG", + "link" : "Surface.convert_alpha", + "datetimeon" : "2009-11-12T10:31:24", + "id" : 3006 + }, + { + "link" : "Surface.convert_alpha", + "content" : "aghahhahahahah I LOVE THIS OMG (*&^%$#@Q", + "user_title" : "Anonymous", + "id" : 3007, + "datetimeon" : "2009-11-12T10:31:43" + }, + { + "link" : "pygame.transform.scale", + "content" : "In my game the screen is scaled. This can cause havoc with the mouse positioning. I made this function:\n\n def get_mouse_pos(pos):\n\treturn (pos[0] * (1280.0/float(game.game_scaled[0])),pos[1] * (720.0/float(game.game_scaled[1])))\n\nReplace 1280.0 and 720.0 with the resolution of the pre-scaled game and game.game_scaled with a sequence containing the scaled resolution.", + "user_title" : "Anonymous", + "id" : 3008, + "datetimeon" : "2009-11-12T12:43:39" + }, + { + "datetimeon" : "2009-06-24T20:28:26", + "id" : 2846, + "content" : "It seems to work if you set the display of the movie to a surface of the same size and then blit that surface to the screen.", + "user_title" : "Anonymous", + "link" : "pygame.movie" + }, + { + "datetimeon" : "2009-11-11T15:11:43", + "id" : 3004, + "user_title" : "Anonymous", + "content" : "This documentation does not seem to match what is currently in 1.8.1. \nInstead: pygame.transform.threshold(DestSurface, Surface, color, threshold = (0,0,0,0), diff_color = (0,0,0,0), change_return = True, Surface =None): return num_threshold_pixels", + "link" : "pygame.transform.threshold" + }, + { + "id" : 2943, + "datetimeon" : "2009-09-11T20:56:38", + "link" : "pygame.Color", + "user_title" : "Anonymous", + "content" : "For some odd reason anything in pygame.Color gets an error message like it doesn't exist.\nIf I try using pygame.Color.r, it says that Color has no attribute r. I tried redownloading\npygame, but nothing diffrent." + }, + { + "datetimeon" : "2009-06-24T20:09:34", + "id" : 2845, + "user_title" : "Anonymous", + "content" : "I am having the same problem on XP. The sound plays, but the video does not.", + "link" : "pygame.movie" + }, + { + "content" : "\"current_h, current_h: Width and height of the current video mode, or of the\"[...]\nOne of them should be \"current_w\" instead.", + "user_title" : "Anonymous", + "link" : "pygame.display.Info", + "datetimeon" : "2009-11-07T06:40:09", + "id" : 2999 + }, + { + "id" : 3000, + "datetimeon" : "2009-11-08T08:17:58", + "link" : "pygame.PixelArray", + "content" : "For me, PixelArray works much faster (4 or 5 times faster) than Surfarray. I wanted to set every pixel in my off-screen surface individually. Creating the surface, creating a PixelArray on it, and going through the pixels one-by-one is much faster than creating the bitmap using numpy and calling surfarray.make_surface.", + "user_title" : "Anonymous" + }, + { + "content" : "?? ìîÊåòå íàðÚñîâàòÌ íåñêîëÌêî ïàðàëëåëÌíûõ ëÚíÚé ðÿÀîì ñ Àðóãîì. ?òîáû Úõ ÀëÚíû ñëÚâàëÚñÌ\n îíÚ áóÀóò âÚÀíû, êàê îÀíà. Íî Üòî áóÀåò ìåÀëåííåé.\n I don't know english, write russian. translate.google for you help! :)", + "user_title" : "Anonymous", + "link" : "pygame.draw.aaline", + "datetimeon" : "2009-11-08T18:19:31", + "id" : 3001 + }, + { + "datetimeon" : "2009-11-11T05:10:30", + "id" : 3002, + "user_title" : "Anonymous", + "content" : "uiuuiu", + "link" : "pygame.key.get_focused" + }, + { + "user_title" : "Anonymous", + "content" : "its very ...................", + "link" : "pygame.key.get_focused", + "datetimeon" : "2009-11-11T05:11:00", + "id" : 3003 + }, + { + "content" : "Tip for noobs. This was killing me. If you're trying this for the first time and getting no sound, it may be because you're program exits before the playback thread completes. See pygame/examples/sound.py: it waits at the end.", + "user_title" : "Anonymous", + "link" : "Channel.play", + "datetimeon" : "2009-12-19T19:22:25", + "id" : 3026 + }, + { + "id" : 3027, + "datetimeon" : "2009-12-19T19:23:52", + "link" : "Sound.play", + "content" : "Tip for noobs. This was killing me. If you're trying this for the first time and\ngetting no sound, it may be because you're program exits before the playback\nthread completes. See pygame/examples/sound.py: it waits at the end.", + "user_title" : "Anonymous" + }, + { + "content" : "You missed pygame.Mixer", + "user_title" : "Anonymous", + "link" : "pygame.init", + "datetimeon" : "2009-10-27T02:47:35", + "id" : 2991 + }, + { + "link" : "pygame.movie", + "user_title" : "Anonymous", + "content" : "Tried this on XP with pygame 1.9 - video works, but sound doesn't. Uninitializing pygame.mixer does not seem to effect behavior.", + "id" : 2992, + "datetimeon" : "2009-10-27T20:31:08" + }, + { + "user_title" : "Anonymous", + "content" : "As of 1.9, it does not appear the loop param exists.", + "link" : "Movie.play", + "datetimeon" : "2009-10-27T20:42:45", + "id" : 2993 + }, + { + "datetimeon" : "2009-10-29T10:53:02", + "id" : 2994, + "content" : "If your movies play slow or occasionally freeze, setting a limit to framerates seems to resolve the problem. \nSee the documentation on Clock.tick(framerate) on how to limit framerates:\nhttp://www.pygame.org/docs/ref/time.html#Clock.tick", + "user_title" : "Anonymous", + "link" : "pygame.movie.Movie" + }, + { + "id" : 2995, + "datetimeon" : "2009-11-04T18:10:25", + "link" : "pygame.scrap.lost", + "user_title" : "Anonymous", + "content" : "The example code and the explanation contradict each other.\n\nThe example returns true if lost, supposedly, and the explanation supposedly returns False if lost?" + }, + { + "link" : "pygame.PixelArray", + "user_title" : "Anonymous", + "content" : "Does not work *at all*. No changes occur when I change any pixels.", + "id" : 2996, + "datetimeon" : "2009-11-05T22:12:49" + }, + { + "datetimeon" : "2009-12-21T19:52:02", + "id" : 3028, + "user_title" : "Anonymous", + "content" : "Remember, there are not semitones. Thus, each semitone is represented by an integer named \"note\".\nIf you are a beginer it is a good choice to start practicing with the middle notes.\nC 4 is the note number 61. Have fun!.\n\nTourette", + "link" : "Output.note_on" + }, + { + "user_title" : "Anonymous", + "content" : "pygame.mask.from_surface(Surface, threshold) -> Mask\nno Keyword argument", + "link" : "pygame.mask.from_surface", + "datetimeon" : "2009-10-25T18:04:44", + "id" : 2989 + }, + { + "datetimeon" : "2009-08-04T11:33:28", + "id" : 2905, + "content" : "you use a class to get the var\nfor example\nclass myimage(object):\n image = (your image)\nthen to blit\n\ndisplay.blit(myimage.image, (0,0))\n\nso that you are only accesing the one variable to blit instead of multiple instances of the same", + "user_title" : "Anonymous", + "link" : "Surface.copy" + }, + { + "link" : "Clock.get_fps", + "content" : "if you want to measure it over a period of your choosing just compute a moving average of the result of clock.tick(), like this:\n\n...\n\nrecent_frame_lengths = [ 100, 100, 100, 100, 100 ]\nrfl_array_len = float( len( recent_frame_lengths ) )\n\n...\nmain():\n...\ndt = clock.tick()\nrecent_frame_lengths.pop(0)\nrecent_frame_lengths.append(dt)\naverage_frame_length = recent_frame_lengths / rfl_array_len\nframes_per_second = 1000. / average_frame_length", + "user_title" : "Anonymous", + "id" : 3124, + "datetimeon" : "2010-05-18T12:08:59" + }, + { + "content" : "button can be at least 1-6. 4/5 are for the scroll wheel. \nThe squeeze-click on new Apple mice is 6.", + "user_title" : "Anonymous", + "link" : "pygame.event", + "datetimeon" : "2010-05-20T22:13:33", + "id" : 3126 + }, + { + "id" : 3127, + "datetimeon" : "2010-05-20T22:31:20", + "link" : "pygame.mouse.get_pressed", + "content" : "event polling:\n#self.keys is [] 256 len. \nself.mouse = ((0,0), 0, 0, 0, 0, 0, 0) #(pos, b1,b2,b3,b4,b5,b6)\n#squeezing a new Apple mouse is button 6. \nfor event in pygame.event.get():\n\tif event.type == pygame.QUIT:\n\t\tself.running = 0\n\telif event.type == pygame.MOUSEBUTTONDOWN:\n\t\tself.mouse[event.button] = 1\n\t\tself.mouse[0] = event.pos\n\telif event.type == pygame.MOUSEBUTTONUP:\n\t\tself.mouse[event.button] = 0\n\t\tself.mouse[0] = event.pos\n\telif event.type == pygame.MOUSEMOTION:\n\t\tself.mouse[0] = event.pos\n\telif event.type == pygame.KEYDOWN:\n\t\tself.keys[event.key % 255] = 1\n\telif event.type == pygame.KEYUP:\n\t\tself.keys[event.key % 255] = 0", + "user_title" : "Anonymous" + }, + { + "link" : "pygame.draw.circle", + "user_title" : "Anonymous", + "content" : "PyGame beginners, please don't look at Matthew N. Brown's code below.\n\nThat is the worst Python code I have ever seen, and it offers almost no examples of how to use pygame.draw.circle.", + "id" : 3123, + "datetimeon" : "2010-05-17T13:22:44" + }, + { + "content" : "What about subsubsubsubsurfaces?", + "user_title" : "Anonymous", + "link" : "Surface.subsurface", + "datetimeon" : "2009-06-14T09:27:19", + "id" : 2830 + }, + { + "content" : "for some reason this gives always wrong numbers.\nmask_creep = pygame.mask.from_surface(creep.image)\nmask_player = pygame.mask.from_surface(p.image)\np.life -= mask_player.overlap_area(\n mask_creep, (creep.rect.x-p.rect.x,creep.rect.y-p.rect.y))\n\np.life is the life of the player, and I want to drain it by the amount of pixels overlapping with creep.\nhowever, it seems it hits before it should, and with mask_creep.invert() I seem to get more accurate hits, which makes no sense...", + "user_title" : "Anonymous", + "link" : "pygame.mask", + "datetimeon" : "2009-10-24T06:03:49", + "id" : 2984 + }, + { + "content" : "get also this warning : nsquickdrawview \nif I compile.\n\ncanŽt use pygame with py2app\n\npython 2.4 or 2.5\nmaxos leopard\neclipse", + "user_title" : "Anonymous", + "link" : "pygame.display.init", + "datetimeon" : "2010-01-05T21:56:27", + "id" : 3032 + }, + { + "link" : "Rect.collidedict", + "content" : "This function is not working as stated. It requires a 'rectstyle' argument (ie. a tuple with rect's parameters). it tests for collisions using this 'rectsytle' object and returns any that are colliding along with their values. Not really useful since no one needs a tuple of rect's parameters back. this would be really nice if it worked as stated...", + "user_title" : "Anonymous", + "id" : 2982, + "datetimeon" : "2009-10-24T03:33:00" + }, + { + "link" : "pygame.transform.rotate", + "content" : "The math isn't that hard, you just have to think in relative coordinates then.\nThe key is to look at the center of the image, as these coordinates wont change by rotating it.\nThe coordinates of the center of the image on the screen are given by:\nx_cntr.. x coordinate of the center\ny_cntr.. y coordinate of the center\n\npos_org = (x_cntr - image_org.get_rect().width / 2,\n y_cntr - image_org.get_rect().height / 2) \n \"\"\"gives position of upper left corner of image_org (not rotated)\n depending on the center coordinates for the Surface.blit function\"\"\"\nimage_rotated = pygame.transform.rotate(image_org, angle) #rotate image\npos_new = (x_pos_org - image_rotated.get_rect().width / 2,\n y_pos_org - image_rotated.get_rect().height / 2)\n #get new position for upper left corner for rotated image", + "user_title" : "Anonymous", + "id" : 3036, + "datetimeon" : "2010-01-21T11:14:00" + }, + { + "link" : "pygame.transform.rotate", + "user_title" : "Anonymous", + "content" : "x_pos_org and y_pos_org naturally need to be x_cntr and y_cntr.. sry", + "id" : 3037, + "datetimeon" : "2010-01-21T11:20:31" + }, + { + "datetimeon" : "2010-01-21T12:35:00", + "id" : 3038, + "user_title" : "Anonymous", + "content" : "fuck u", + "link" : "PixelArray.replace" + }, + { + "user_title" : "Anonymous", + "content" : "doesnt work!", + "link" : "pygame.display.set_caption", + "datetimeon" : "2010-01-25T12:07:07", + "id" : 3039 + }, + { + "id" : 3040, + "datetimeon" : "2010-01-25T12:07:16", + "link" : "pygame.display.set_caption", + "user_title" : "Anonymous", + "content" : "WTH!???" + }, + { + "link" : "pygame.image.load", + "user_title" : "Anonymous", + "content" : "Where should the image file be save at?", + "id" : 3042, + "datetimeon" : "2010-01-29T02:01:23" + }, + { + "id" : 3043, + "datetimeon" : "2010-01-30T14:45:56", + "link" : "Surface.blit", + "content" : "To clip the blit, you have to pass a rect like this (0, 0, clipWidth, clipHeigth):\n\nexample:\n\nsForeground.blit(sText, rText, (0, 0, 32, 32)):\n\ndraw the surface sText into sForeground at topleft position defined with the rect rText,\nclippping the sText by 32x32 pixel box", + "user_title" : "Anonymous" + }, + { + "id" : 3044, + "datetimeon" : "2010-02-02T15:56:23", + "link" : "pygame.display.set_caption", + "content" : "actually, it DOES WORK. put it in the right group", + "user_title" : "Anonymous" + }, + { + "id" : 3045, + "datetimeon" : "2010-02-02T23:04:45", + "link" : "Rect.move", + "user_title" : "Anonymous", + "content" : "how does this even work?????" + }, + { + "link" : "pygame.transform.smoothscale", + "user_title" : "Anonymous", + "content" : "Pygame 1.9: DeprecationWarning: integer argument expected, got float\n\nThe tuple elements must be integers. The line number in the warning message will\nindicate your enclosing function or method. I found this very misleading and\nfrustrating.", + "id" : 3048, + "datetimeon" : "2010-02-06T02:16:05" + }, + { + "datetimeon" : "2010-02-08T10:30:27", + "id" : 3049, + "content" : "By calling Clock.tick -> clock.tick", + "user_title" : "Anonymous", + "link" : "Clock.tick_busy_loop" + }, + { + "id" : 3050, + "datetimeon" : "2010-02-08T22:31:47", + "link" : "pygame.draw.circle", + "user_title" : "Anonymous", + "content" : "that code you posted (TWICE) doesnt show how to draw a circle in pygame.\n It shows an example of a complex couple hundred lines of interactive \nphysics engine that just so happens to take im guessing less than\n5 lines to use pygame.draw.circle. Its a waste of space Good job for getting your code out into \nthe world. Now people dont like you." + }, + { + "datetimeon" : "2009-10-22T05:21:56", + "id" : 2977, + "content" : "TypeError: descriptor 'collidelistall' requires a 'pygame.Rect' object but received a 'list'\n\n...so it doesn't like a list, but a pygame.Rect? That does't make sense.", + "user_title" : "Anonymous", + "link" : "Rect.collidelistall" + }, + { + "link" : "pygame.Surface", + "user_title" : "Anonymous", + "content" : "Also using\n\n my_surface=pygame.Surface([width, height]).convert()\n\nseems to be just as effective", + "id" : 2974, + "datetimeon" : "2009-10-16T22:13:15" + }, + { + "content" : "x = x2 - x1, y = y2 - y1 gives the width and height of the rectangle.\n(x2 - x1) / 2, (y2 - y1) / 2 gives the center coordinates.\nI don't know of a built-in method on the Rect that gives the center coords.", + "user_title" : "Anonymous", + "link" : "pygame.draw.rect", + "datetimeon" : "2009-10-21T20:50:31", + "id" : 2976 + }, + { + "datetimeon" : "2009-10-08T11:50:27", + "id" : 2967, + "user_title" : "Anonymous", + "content" : "AWSOME", + "link" : "pygame.draw" + }, + { + "datetimeon" : "2009-09-20T07:33:05", + "id" : 2954, + "content" : "Aloha! jdv", + "user_title" : "Anonymous", + "link" : "Rect.move_ip" + }, + { + "id" : 2964, + "datetimeon" : "2009-10-01T14:16:05", + "link" : "pygame.draw.arc", + "user_title" : "Anonymous", + "content" : "Worth mentioning: the initial angle must be less than the final angle; otherwise it will draw the full elipse." + }, + { + "link" : "pygame.draw.circle", + "user_title" : "Anonymous", + "content" : "If you want a circle with a *good* outline, use this:\n\ndef drawcircle(image, colour, origin, radius, width=0):\n\tif width == 0:\n\t\tpygame.draw.circle(image,colour,intlist(origin),int(radius))\n\telse:\n\t\tif radius > 65534/5: radius = 65534/5\n\t\tcircle = pygame.Surface([radius*2+width,radius*2+width]).convert_alpha()\n\t\tcircle.fill([0,0,0,0])\n\t\tpygame.draw.circle(circle, colour, intlist([circle.get_width()/2, circle.get_height()/2]), int(radius+(width/2)))\n\t\tif int(radius-(width/2)) > 0: pygame.draw.circle(circle, [0,0,0,0], intlist([circle.get_width()/2, circle.get_height()/2]), abs(int(radius-(width/2))))\n\t\timage.blit(circle, [origin[0] - (circle.get_width()/2), origin[1] - (circle.get_height()/2)])", + "id" : 2963, + "datetimeon" : "2009-09-30T10:47:07" + }, + { + "datetimeon" : "2009-09-21T11:39:31", + "id" : 2956, + "user_title" : "Anonymous", + "content" : "I think the problem has something to do with encapsulation.\nTry:\ncurrent_song = 0\ndef Play_Next_Song():\n global current_song\n if pygame.mixer.music.get_busy() == False:\n print songs[current_song]\n pygame.mixer.music.load(songs[current_song])\n pygame.mixer.music.play() \n current_song += 1", + "link" : "pygame.mixer.music.load" + }, + { + "content" : "WHAT?", + "user_title" : "Anonymous", + "link" : "pygame.draw.lines", + "datetimeon" : "2009-09-23T15:22:38", + "id" : 2957 + }, + { + "datetimeon" : "2009-09-24T13:53:40", + "id" : 2958, + "content" : "Can I draw circles too?", + "user_title" : "Anonymous", + "link" : "pygame.draw.circle" + }, + { + "user_title" : "Anonymous", + "content" : "how can i get a mouse wheel value? please show in a snippet - i don't know how to use pygame.MOUSEBUTTONDOWN and pygame.MOUSEBUTTONUP", + "link" : "pygame.mouse.get_pressed", + "datetimeon" : "2009-09-25T11:53:07", + "id" : 2960 + }, + { + "user_title" : "Anonymous", + "content" : "Setting the line width does not work!", + "link" : "pygame.draw.rect", + "datetimeon" : "2009-09-29T10:21:37", + "id" : 2962 + }, + { + "id" : 3128, + "datetimeon" : "2010-05-21T11:08:52", + "link" : "Rect.collidelistall", + "content" : "i dont like pygame very much\nrectangles can go poop themselves", + "user_title" : "Anonymous" + }, + { + "link" : "Clock.tick_busy_loop", + "user_title" : "Anonymous", + "content" : "In fact it should be\n\"By calling Clock.tick(40)\" -> \"Clock.tick_busy_loop(40)\"", + "id" : 3130, + "datetimeon" : "2010-06-01T03:26:55" + }, + { + "datetimeon" : "2010-06-01T07:55:40", + "id" : 3131, + "content" : "No, waiting at the very beginning or very end of a loop does not make\nmuch difference. Moving it to the middle might: \n screen.fill(...)\n clock.tick(30)\n screen.blit(...)\nwill mostly show a blank screen because the fill will be visible\nduring the wait.", + "user_title" : "Anonymous", + "link" : "Clock.tick" + }, + { + "id" : 3133, + "datetimeon" : "2010-06-02T03:11:33", + "link" : "Rect.collidelist", + "content" : "haleluja", + "user_title" : "Anonymous" + }, + { + "link" : "pygame.PixelArray", + "content" : "PixelArray works faster than SurfArray for me also.", + "user_title" : "Anonymous", + "id" : 3134, + "datetimeon" : "2010-06-02T20:55:16" + }, + { + "id" : 3135, + "datetimeon" : "2010-06-06T16:23:37", + "link" : "pygame.mixer.music.fadeout", + "user_title" : "Anonymous", + "content" : "Only seems to block if you start another piece of music playing while it's still\nfading out." + }, + { + "link" : "Surface.subsurface", + "user_title" : "Anonymous", + "content" : "If you're having trouble with color keys, try image.set_alpha(None) on each individual subsurface.\nsubsurface seems not to always inherit its parent's alpha setting, so if the parent source image has an alpha then color key is ignored in subsurfaces.\n\nYou can easily remove the alpha channel in GIMP by right-clicking the background layer and selecting \"remove alpha channel\" to fix all your problems also :)", + "id" : 3139, + "datetimeon" : "2010-06-08T23:09:21" + }, + { + "datetimeon" : "2010-06-10T20:42:30", + "id" : 3140, + "user_title" : "Anonymous", + "content" : "You should use pygame.display.Info.current_h and pygame.display.Info.current_w.", + "link" : "pygame.display.get_surface" + }, + { + "user_title" : "Anonymous", + "content" : "pygame.display.Info().current_h and pygame.display.Info().current_w", + "link" : "pygame.display.get_surface", + "datetimeon" : "2010-06-10T20:43:14", + "id" : 3141 + }, + { + "link" : "pygame.display.get_surface", + "user_title" : "Anonymous", + "content" : "screen = pygame.display.set_mode(...)\nscreen.get_size()", + "id" : 3142, + "datetimeon" : "2010-06-10T20:55:19" + }, + { + "content" : "11246579455877\nppqu s442", + "user_title" : "Anonymous", + "link" : "pygame.mixer.music.play", + "datetimeon" : "2010-06-11T18:56:04", + "id" : 3143 + }, + { + "link" : "Color.r", + "content" : "import pygame\nimport pygame, sys,os, time \nfrom pygame.locals import * \nfrom pygame.color import THECOLORS \nimport platform, os \nif platform.system()==\"Windows\": \n os.environ['SDL_VIDEODRIVER']='windib'\npygame.init()\nwindow = pygame.display.set_mode((600,600)) \nscreen = pygame.display.get_surface() \npygame.display.set_caption('Excercise 5') \nscreen.fill((0,0,0))\n\nclass GfxCursor:\n \"\"\"\n Replaces the normal pygame cursor with any bitmap cursor\n \"\"\"\n\n def __init__(self,surface,cursor=None,hotspot=(0,0)):\n \"\"\"\n surface = Global surface to draw on\n cursor = surface of cursor (needs to be specified when enabled!)\n hotspot = the hotspot for your cursor\n \"\"\"\n self.surface = surface\n self.enabled = 0\n self.cursor = None\n self.hotspot = hotspot\n self.bg = None\n self.offset = 0,0\n self.old_pos = 0,0\n \n if cursor:\n self.setCursor(cursor,hotspot)\n self.enable()\n\n def enable(self):\n \"\"\"\n Enable the GfxCursor (disable normal pygame cursor)\n \"\"\"\n if not self.cursor or self.enabled: return\n pygame.mouse.set_visible(0)\n self.enabled = 1\n\n def disable(self):\n \"\"\"\n Disable the GfxCursor (enable normal pygame cursor)\n \"\"\"\n if self.enabled:\n self.hide()\n pygame.mouse.set_visible(1)\n self.enabled = 0\n\n def setCursor(self,cursor,hotspot=(0,0)):\n \"\"\"\n Set a new cursor surface\n \"\"\"\n if not cursor: return\n self.cursor = cursor\n self.hide()\n self.show()\n self.offset = 0,0\n self.bg = pygame.Surface(self.cursor.get_size())\n pos = self.old_pos[0]-self.offset[0],self.old_pos[1]-self.offset[1]\n self.bg.blit(self.surface,(0,0),\n (pos[0],pos[1],self.cursor.get_width(),self.cursor.get_height()))\n\n self.offset = hotspot\n\n def setHotspot(self,pos):\n \"\"\"\n Set a new hotspot for the cursor\n \"\"\"\n self.hide()\n self.offset = pos\n\n def hide(self):\n \"\"\"\n Hide the cursor (useful for redraws)\n \"\"\"\n if self.bg and self.enabled:\n return self.surface.blit(self.bg,\n (self.old_pos[0]-self.offset[0],self.old_pos[1]-self.offset[1]))\n\n def show(self):\n \"\"\"\n Show the cursor again\n \"\"\"\n if self.bg and self.enabled:\n pos = self.old_pos[0]-self.offset[0],self.old_pos[1]-self.offset[1]\n self.bg.blit(self.surface,(0,0),\n (pos[0],pos[1],self.cursor.get_width(),self.cursor.get_height()))\n return self.surface.blit(self.cursor,pos)\n\n def update(self,event):\n \"\"\"\n Update the cursor with a MOUSEMOTION event\n \"\"\"\n self.old_pos = event.pos\n\nif __name__ == '__main__': #test it out\n import pygame.draw\n pygame.init()\n screen = pygame.display.set_mode((400, 300))\n screen.fill((50, 50, 111), (0, 0, 400, 150))\n pygame.display.flip()\n pygame.display.set_caption('Test the GfxCursor (and paint)')\n \n image = pygame.Surface((20, 20))\n pygame.draw.circle(image, (50, 220, 100), (10, 10), 8, 0)\n pygame.draw.circle(image, (220, 200, 50), (10, 10), 8, 2)\n image.set_at((9, 9), (255,255,255))\n image.set_colorkey(0, pygame.RLEACCEL)\n \n magicbox = pygame.Rect(10, 10, 100, 90)\n magiccolor = 0\n \n cursor = GfxCursor(screen, image, (10, 10))\n finished = 0\n downpos = None\n while not finished:\n dirtyrects = []\n dirtyrects.extend([cursor.hide()])\n for e in pygame.event.get():\n if e.type in (pygame.QUIT, pygame.KEYDOWN):\n finished = 1\n break\n elif e.type == pygame.MOUSEBUTTONDOWN:\n cursor.disable()\n downpos = e.pos\n elif e.type == pygame.MOUSEBUTTONUP:\n cursor.enable()\n downpos = None\n elif downpos and e.type == pygame.MOUSEMOTION:\n r = pygame.draw.line(screen, (100,100,100), downpos, e.pos, 2)\n dirtyrects.append(r)\n downpos = e.pos\n cursor.update(e)\n elif not downpos and e.type == pygame.MOUSEMOTION:\n cursor.update(e)\n \n magiccolor = (magiccolor + 2) % 255\n r = screen.fill((0, 0, magiccolor), magicbox)\n dirtyrects.append(r)\n \n #here's how we sandwich the flip/update with cursor show and hide\n dirtyrects.extend([cursor.show()])\n pygame.display.update(dirtyrects)\n \n pygame.time.delay(5) #should be time.wait(5) with pygame-1.3 :]", + "user_title" : "Anonymous", + "id" : 3145, + "datetimeon" : "2010-06-13T02:45:37" + }, + { + "link" : "pygame.font.get_fonts", + "content" : "I get an 'UnboundLocalError: local variable 'fonts' referenced before assignment' error. \nI'm on Mac OS X 10.4 Tiger with Python 2.6.4, Pygame 1.9.1.", + "user_title" : "Anonymous", + "id" : 3146, + "datetimeon" : "2010-06-19T22:22:14" + }, + { + "link" : "Joystick.get_axis", + "user_title" : "Anonymous", + "content" : "I'm no expert but wouldn't it be faster to use the math.hypot(a, b)\nfunction for determining length instead of writing your own function?", + "id" : 2759, + "datetimeon" : "2009-06-09T21:45:55" + }, + { + "id" : 2758, + "datetimeon" : "2009-06-08T14:31:39", + "link" : "pygame.transform.rotate", + "user_title" : "Anonymous", + "content" : "You can also move the corner:\n\nsquare_corner=(x-square_dim[0]*sqrt(2)/2*sin((abs(angle)+45)*pi/180),\n y-square_dim[1]*sqrt(2)/2*sin((45+abs(angle))*pi/180))\n screen.blit(pygame.transform.rotate(square,angle), square_corner)\n\nBeware: sin calculates the angle in rad but rotate needs angles in degrees." + }, + { + "id" : 2755, + "datetimeon" : "2009-06-03T22:15:32", + "link" : "pygame.event", + "user_title" : "Anonymous", + "content" : "ACTIVEEVENT has two attributes. \"gain\" is set to 0 or one depending if the type of focus was lost or gained. \"state\" will equal 1 for mouse focus, 2 for keyboard focus, or 4 for window iconification." + }, + { + "content" : "If you want only certain events to be enabled -- you will need to disable them all, and only then enable the ones that you need:\n#--\npygame.event.set_allowed(None)\nprint map(pygame.event.get_blocked,range(1,33))\npygame.event.set_allowed([pygame.QUIT, pygame.KEYDOWN, pygame.USEREVENT])\nprint map(pygame.event.get_blocked,range(1,33))\n#--\nbelow is the output:\n[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n[1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1]", + "user_title" : "Anonymous", + "link" : "pygame.event.set_allowed", + "datetimeon" : "2009-05-30T16:39:22", + "id" : 2754 + }, + { + "id" : 2753, + "datetimeon" : "2009-05-27T02:45:25", + "link" : "Surface.unmap_rgb", + "content" : "Also, it should maybe be noted that it returns 4 tuple items, not just 3.\nMy guess is RGBA tuple instead of RGB, but I'm not an expert :P", + "user_title" : "Anonymous" + }, + { + "link" : "Surface.unmap_rgb", + "content" : "convert a mapped integer color value into a Color\nSurface.map_rgb(mapped_int): return Color\n ^\nShouldn't it be \"Surface.unmap_rgb\"?", + "user_title" : "Anonymous", + "id" : 2752, + "datetimeon" : "2009-05-27T02:43:16" + }, + { + "id" : 2751, + "datetimeon" : "2009-05-21T06:41:14", + "link" : "pygame.mixer.music", + "user_title" : "Anonymous", + "content" : "Tracked module playback with Pygame has a lower playback volume than usual,\nand I need to find something like \"stereo separation\" feature yet." + }, + { + "datetimeon" : "2009-05-21T06:35:19", + "id" : 2750, + "user_title" : "Anonymous", + "content" : "Yes, I just found it out, and it plays tracked music modules such as MOD or XM.\nBut IT (Impulse Tracker) modules don't play correctly. Wow. I used fmod with Python bindings\nall the time, and Pygame supported it already. I wish I knew that sooner :D", + "link" : "pygame.mixer.music" + }, + { + "datetimeon" : "2009-05-19T11:35:42", + "id" : 2749, + "content" : "While using this, it seems that it returns true when the music is paused. Anyone else having this problem, if it's a problem?", + "user_title" : "Anonymous", + "link" : "pygame.mixer.music.get_busy" + }, + { + "datetimeon" : "2009-05-11T00:29:55", + "id" : 2744, + "content" : "If you want to draw the sprites in your group in the opposite order you could try something like this:\n#Suppose that you keep a list of objects in \"Sprites\"\nSprites=[]\n#You also have:\nAllsprites=pygame.sprite.OrderedUpdates(Sprites)\n\nWhenever you add something to the list of sprites, you want to add it to Allsprites like this:\ndef create_sprite():\n a=SomeClassForYourSprite()\n Sprites.append(a)\n Allsprites.add(a)\n\n#That is how I normally add sprites, but unfortunately I was getting the reverse order of what I wanted so I did this:\ndef create_sprite():\n Allsprites.empty() #This removes all objects from your group\n a=SomeClassForYourSprite()\n Sprites.insert(0,a) #Placing your new sprite at the front of the list\n for sprite in Sprites:\n Allsprites.add(sprite)\n\nThis should reverse the order for you, allowing the newest sprite created to appear at the bottom instead of the top", + "user_title" : "Anonymous", + "link" : "pygame.sprite.OrderedUpdates" + }, + { + "datetimeon" : "2011-01-14T19:38:50", + "id" : 3730, + "content" : "Is this function blocking? I mean... when it returns and my program flow continues, can I be assured that the display has updated on the actual screen?", + "user_title" : "Anonymous", + "link" : "pygame.display.update" + }, + { + "id" : 3734, + "datetimeon" : "2011-01-16T15:32:54", + "link" : "pygame.font.SysFont", + "user_title" : "Anonymous", + "content" : "Is there a way to set a path to a font file?" + }, + { + "datetimeon" : "2011-01-16T19:25:25", + "id" : 3735, + "user_title" : "Anonymous", + "content" : "eee", + "link" : "pygame.image.tostring" + }, + { + "id" : 3737, + "datetimeon" : "2011-01-17T21:08:17", + "link" : "pygame.draw.polygon", + "content" : "The last comment was spam", + "user_title" : "Anonymous" + }, + { + "user_title" : "Anonymous", + "content" : "[(x,y), (x1,y1), (x2,y2)]", + "link" : "pygame.draw.polygon", + "datetimeon" : "2011-01-17T21:08:51", + "id" : 3738 + }, + { + "id" : 3739, + "datetimeon" : "2011-01-18T03:41:16", + "link" : "pygame.transform.smoothscale", + "user_title" : "Anonymous", + "content" : "The core algorithm works with 32-bit surfaces. When a 24-bit surface is passed, the pixel data is converted to 32-bit data before the actual transformation, and then it's converted back into 24-bits again, which means 2 extra conversions of the whole image. This would especially be troublesome with large images." + }, + { + "datetimeon" : "2011-01-18T06:48:13", + "id" : 3740, + "user_title" : "Anonymous", + "content" : "re", + "link" : "pygame.mixer.music.pause" + }, + { + "link" : "index.html", + "user_title" : "Anonymous", + "content" : "Hello world", + "id" : 3743, + "datetimeon" : "2011-01-19T16:38:01" + }, + { + "link" : "pygame.event.get_grab", + "content" : "what", + "user_title" : "Anonymous", + "id" : 3748, + "datetimeon" : "2011-01-25T20:39:22" + }, + { + "link" : "Font.size", + "content" : "The width for \"ae\" WILL always match the width for \"a\" + \"e\" (which is \"a\" concatinated with\"e\").\nIt will not always match the width of \"a\" plus the width of \"e\".\n(But we knew what you meant.)", + "user_title" : "Anonymous", + "id" : 3749, + "datetimeon" : "2011-01-25T21:11:43" + }, + { + "datetimeon" : "2011-01-27T17:25:55", + "id" : 3754, + "content" : "can we get an admin to delete that?", + "user_title" : "Anonymous", + "link" : "pygame.draw.circle" + }, + { + "link" : "PixelArray.compare", + "content" : "This function seems to me little bit buggy, so I wrote my own:\n\na and b are surfarrays of some surfaces that you want to compare\n\n def comparray(self,a,b):\n c = abs(a.__sub__(b))\n c = c.__ge__(self.tolerance)*255\n surface = pygame.surfarray.make_surface(c)\n return surface", + "user_title" : "Anonymous", + "id" : 3755, + "datetimeon" : "2011-01-27T18:49:07" + }, + { + "datetimeon" : "2011-01-28T04:28:47", + "id" : 3758, + "user_title" : "Anonymous", + "content" : "your gay", + "link" : "Rect.colliderect" + }, + { + "id" : 3765, + "datetimeon" : "2011-01-30T21:39:27", + "link" : "Channel.queue", + "content" : "It would be nice if the number of Sounds on queue was more than one...\n\nGreg Ruo", + "user_title" : "Anonymous" + }, + { + "link" : "Channel.queue", + "content" : "I solved my previous question:\n\nIf you need to play in sequence several Sounds in a queue, you can solve this\nwith something like:\n=======================\n i=0\n while (Ch0.get_queue()==None) and (i<10):\n i+=1\n Ch0.queue(f[i])\n============================\n\nwhere Ch0 is your Sound channel created with Ch0=pygame.mixer.Channel(0).\nIn other words, even if the queue allows only one single sound in queue,\nI use the .get_queue method to wait until the queue is free before\n adding the next Sound in the sequence.\n\nIf you have better solutions please reply here. Thanks.\n\nGreg Ruo", + "user_title" : "Anonymous", + "id" : 3766, + "datetimeon" : "2011-01-30T22:38:45" + }, + { + "link" : "pygame.event.get", + "content" : "Probably would be good to have an OPTIONAL choice to remove them from the queue . . .", + "user_title" : "Anonymous", + "id" : 3768, + "datetimeon" : "2011-01-31T13:48:40" + }, + { + "content" : "I had this weird thing where blue/red was inversed, but not the other colours, when I was mapping some pixels from one image to a blank surface.\nIt was caused by copying the color integer directly to one pixel to the other, so the trick is to always surface.unmap_rgb(pixel) before setting the color to a new pixel \nThat tricked works.\nIt's the only way unfortunately.", + "user_title" : "Anonymous", + "link" : "pygame.PixelArray", + "datetimeon" : "2011-02-04T02:33:15", + "id" : 3771 + }, + { + "content" : "@Dave: Thanks very much for writing that we should ignore the keyword \"width\". This saved me time and my program now works.", + "user_title" : "Anonymous", + "link" : "pygame.draw.rect", + "datetimeon" : "2011-02-05T08:25:19", + "id" : 3773 + }, + { + "link" : "Rect.colliderect", + "user_title" : "Anonymous", + "content" : "ankit sucks", + "id" : 3776, + "datetimeon" : "2011-02-08T14:07:31" + }, + { + "link" : "Rect.collidepoint", + "user_title" : "Anonymous", + "content" : "i heard ankit really sucks", + "id" : 3777, + "datetimeon" : "2011-02-08T14:07:46" + }, + { + "user_title" : "Anonymous", + "content" : "ankit tandon sucks", + "link" : "Rect.contains", + "datetimeon" : "2011-02-08T14:07:55", + "id" : 3778 + }, + { + "datetimeon" : "2011-02-09T22:32:43", + "id" : 3779, + "user_title" : "Anonymous", + "content" : "I don't get what this does...", + "link" : "pygame.event.pump" + }, + { + "id" : 3780, + "datetimeon" : "2011-02-10T21:20:05", + "link" : "pygame.event.pump", + "user_title" : "Anonymous", + "content" : "If you don't use the event queue(why aren't you??) this will keep your program from locking up." + }, + { + "content" : "When the camera is stopped and you try to access it pygame segfaults.\n(On Debian testing with pygame 1.9.1)", + "user_title" : "Anonymous", + "link" : "Camera.stop", + "datetimeon" : "2011-02-11T03:31:32", + "id" : 3781 + }, + { + "link" : "pygame.event.set_allowed", + "content" : "set_allowed removes events from the queue! \nEven if the event in question doesn't belong to the given type.\n\n>>> import pygame\n>>> pygame.init()\n>>> pygame.event.post(pygame.event.Event(pygame.USEREVENT, code=0))\n>>> print pygame.event.peek(pygame.USEREVENT)\n1\n>>> pygame.event.set_allowed(pygame.MOUSEMOTION)\n>>> print pygame.event.peek(pygame.USEREVENT)\n0", + "user_title" : "Anonymous", + "id" : 3782, + "datetimeon" : "2011-02-11T17:42:34" + }, + { + "id" : 3783, + "datetimeon" : "2011-02-14T16:59:41", + "link" : "PixelArray.compare", + "user_title" : "Anonymous", + "content" : "Thank you very much.\nYour compare function works much better than the original one." + }, + { + "link" : "Color.g", + "user_title" : "Anonymous", + "content" : "meto", + "id" : 3786, + "datetimeon" : "2011-02-15T19:30:06" + }, + { + "id" : 3787, + "datetimeon" : "2011-02-16T06:49:19", + "link" : "pygame.draw.rect", + "content" : "rofl rofl what for a crappy thing you performed? go to wikipedia it works!\n\nu mad!", + "user_title" : "Anonymous" + }, + { + "user_title" : "Anonymous", + "content" : "rofl rofl what for a crappy thing you performed? go to wikipedia it works!\n\nu mad!", + "link" : "pygame.draw.rect", + "datetimeon" : "2011-02-16T06:50:00", + "id" : 3788 + }, + { + "content" : "stfu\n\nu mad", + "user_title" : "Anonymous", + "link" : "Rect.unionall_ip", + "datetimeon" : "2011-02-16T06:52:07", + "id" : 3789 + }, + { + "id" : 3791, + "datetimeon" : "2011-02-19T12:17:58", + "link" : "pygame.mixer.music.queue", + "user_title" : "Anonymous", + "content" : "This method only queues one music file.\nIf you call it and there already is a queued file, it will be overrided." + }, + { + "id" : 3793, + "datetimeon" : "2011-02-21T08:30:17", + "link" : "pygame.key", + "content" : "elif event.type == pygame.QUIT or event.type == pygame.K_ESCAPE:\n\nthe event type is not pygame.K_ESCAPE. you have to check for a KEYDOWN or KEYUP event and check if it is the key you want, for example:\n\nelif event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):", + "user_title" : "Anonymous" + }, + { + "link" : "pygame.cursors.compile", + "user_title" : "Anonymous", + "content" : "Well, sometimes black and white are swapped, sometimes they aren't.\nEven in the same program, for one mouse cursor it may work right,\nand for another cursor the black & white colors are swapped.\n\nI haven't figured out what triggers this bug.", + "id" : 3797, + "datetimeon" : "2011-02-24T13:00:58" + }, + { + "datetimeon" : "2011-02-26T15:00:47", + "id" : 3798, + "content" : "spam", + "user_title" : "Anonymous", + "link" : "pygame.event.set_grab" + }, + { + "user_title" : "Anonymous", + "content" : "In Pygame 1.9.2 surface objects have sprouted a new method, get_view:\n\nSurface.get_view\nreturn a view of a surface's pixel data.\nSurface.get_view(kind='2'): return\n\nReturn an object which exposes a surface's internal pixel buffer to a NumPy array. For now a custom object with an array struct interface is returned. A Python memoryview may be returned in the future. The buffer is writeable.\n\nThe kind argument is the length 1 string '2', '3', 'r', 'g', 'b', or 'a'. The letters are case insensitive; 'A' will work as well. The argument can be either a Unicode or byte (char) string. The default is '2'.\n\nA kind '2' view is a (surface-width, surface-height) array of raw pixels. The pixels are surface bytesized unsigned integers. The pixel format is surface specific. It is unavailable for 24-bit surfaces.\n\n'3' returns a (surface-width, surface-height, 3) view of RGB color components. Each of the red, green, and blue components are unsigned bytes. Only 24-bit and 32-bit surfaces are supported. The color components must be in either RGB or BGR order within the pixel.\n\n'r' for red, 'g' for green, 'b' for blue, and 'a' for alpha return a (surface-width, surface-height) view of a single color component within a surface: a color plane. Color components are unsigned bytes. Both 24-bit and 32-bit surfaces support 'r', 'g', and 'b'. Only 32-bit surfaces with SRCALPHA support 'a'.\n\nThis method implicitly locks the Surface. The lock will be released, once the returned view object is deleted.", + "link" : "pygame.Surface", + "datetimeon" : "2011-03-01T14:59:53", + "id" : 3803 + }, + { + "datetimeon" : "2011-03-01T15:02:04", + "id" : 3804, + "user_title" : "Anonymous", + "content" : "In Pygame 1.9.2 surface objects have sprouted a new method, get_view:\n\nSurface.get_view\nreturn a view of a surface's pixel data.\nSurface.get_view(kind='2'): return\n\nReturn an object which exposes a surface's internal pixel buffer to a NumPy\narray. For now a custom object with an array struct interface is returned.\nA Python memoryview may be returned in the future. The buffer is writeable.\n\nThe kind argument is the length 1 string '2', '3', 'r', 'g', 'b', or 'a'.\nThe letters are case insensitive; 'A' will work as well. The argument can be\neither a Unicode or byte (char) string. The default is '2'.\n\nA kind '2' view is a (surface-width, surface-height) array of raw pixels. The\npixels are surface bytesized unsigned integers. The pixel format is surface\nspecific. It is unavailable for 24-bit surfaces.\n\n'3' returns a (surface-width, surface-height, 3) view of RGB color components.\nEach of the red, green, and blue components are unsigned bytes. Only 24-bit and\n32-bit surfaces are supported. The color components must be in either RGB or\nBGR order within the pixel.\n\n'r' for red, 'g' for green, 'b' for blue, and 'a' for alpha return a\n(surface-width, surface-height) view of a single color component within a\nsurface: a color plane. Color components are unsigned bytes. Both 24-bit and\n32-bit surfaces support 'r', 'g', and 'b'. Only 32-bit surfaces with SRCALPHA\nsupport 'a'.\n\nThis method implicitly locks the Surface. The lock will be released, once the\nreturned view object is deleted.", + "link" : "pygame.Surface" + }, + { + "user_title" : "Anonymous", + "content" : "for statement with arc", + "link" : "pygame.draw.arc", + "datetimeon" : "2011-03-01T19:54:13", + "id" : 3805 + }, + { + "content" : "Oh my god I was so thankful that you can adjust the volume, I have no editing software to fix my sounds.", + "user_title" : "Anonymous", + "link" : "Sound.set_volume", + "datetimeon" : "2011-03-03T21:26:40", + "id" : 3806 + }, + { + "datetimeon" : "2011-03-09T16:10:36", + "id" : 3808, + "content" : "Thanks to the comments for wrinting about the event attributes and the key constants list. It should be in any serious documentation.", + "user_title" : "Anonymous", + "link" : "pygame.event.Event" + }, + { + "datetimeon" : "2011-03-10T18:41:39", + "id" : 3809, + "user_title" : "Anonymous", + "content" : "a;rtawkljethlak", + "link" : "pygame.font" + }, + { + "datetimeon" : "2011-03-10T18:42:20", + "id" : 3810, + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "link" : "pygame.font.init" + }, + { + "id" : 3811, + "datetimeon" : "2011-03-10T18:42:22", + "link" : "pygame.font.init", + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous" + }, + { + "link" : "pygame.font.init", + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "id" : 3812, + "datetimeon" : "2011-03-10T18:42:24" + }, + { + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "link" : "pygame.font.init", + "datetimeon" : "2011-03-10T18:42:27", + "id" : 3813 + }, + { + "link" : "pygame.font.init", + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "id" : 3814, + "datetimeon" : "2011-03-10T18:42:29" + }, + { + "link" : "pygame.font.init", + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "id" : 3815, + "datetimeon" : "2011-03-10T18:42:31" + }, + { + "id" : 3816, + "datetimeon" : "2011-03-10T18:42:33", + "link" : "pygame.font.init", + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM" + }, + { + "id" : 3817, + "datetimeon" : "2011-03-10T18:42:36", + "link" : "pygame.font.init", + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM" + }, + { + "datetimeon" : "2011-03-10T18:42:37", + "id" : 3818, + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "link" : "pygame.font.init" + }, + { + "datetimeon" : "2011-03-10T18:42:47", + "id" : 3819, + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "link" : "pygame.font.init" + }, + { + "datetimeon" : "2011-03-10T18:42:53", + "id" : 3820, + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "link" : "pygame.font.init" + }, + { + "id" : 3821, + "datetimeon" : "2011-03-10T18:42:54", + "link" : "pygame.font.init", + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous" + }, + { + "id" : 3822, + "datetimeon" : "2011-03-10T18:42:56", + "link" : "pygame.font.init", + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous" + }, + { + "link" : "pygame.font.init", + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "id" : 3823, + "datetimeon" : "2011-03-10T18:42:58" + }, + { + "id" : 3824, + "datetimeon" : "2011-03-10T18:43:01", + "link" : "pygame.font.init", + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM" + }, + { + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "link" : "pygame.font.init", + "datetimeon" : "2011-03-10T18:43:03", + "id" : 3825 + }, + { + "datetimeon" : "2011-03-10T18:43:07", + "id" : 3826, + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "link" : "pygame.font.init" + }, + { + "link" : "pygame.font.init", + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "id" : 3827, + "datetimeon" : "2011-03-10T18:43:09" + }, + { + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "link" : "pygame.font.init", + "datetimeon" : "2011-03-10T18:43:11", + "id" : 3828 + }, + { + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "link" : "pygame.font.init", + "datetimeon" : "2011-03-10T18:43:13", + "id" : 3829 + }, + { + "id" : 3830, + "datetimeon" : "2011-03-10T18:43:15", + "link" : "pygame.font.init", + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM" + }, + { + "datetimeon" : "2011-03-10T18:43:16", + "id" : 3831, + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "link" : "pygame.font.init" + }, + { + "datetimeon" : "2011-03-10T18:43:19", + "id" : 3832, + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "link" : "pygame.font.init" + }, + { + "datetimeon" : "2011-03-10T18:43:20", + "id" : 3833, + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "link" : "pygame.font.init" + }, + { + "link" : "pygame.font.init", + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "id" : 3834, + "datetimeon" : "2011-03-10T18:43:22" + }, + { + "datetimeon" : "2011-03-10T18:43:24", + "id" : 3835, + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "link" : "pygame.font.init" + }, + { + "id" : 3836, + "datetimeon" : "2011-03-10T18:43:26", + "link" : "pygame.font.init", + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM" + }, + { + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "link" : "pygame.font.init", + "datetimeon" : "2011-03-10T18:43:28", + "id" : 3837 + }, + { + "link" : "pygame.font.init", + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "id" : 3838, + "datetimeon" : "2011-03-10T18:43:29" + }, + { + "datetimeon" : "2011-03-10T18:43:31", + "id" : 3839, + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "link" : "pygame.font.init" + }, + { + "id" : 3840, + "datetimeon" : "2011-03-10T18:43:33", + "link" : "pygame.font.init", + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM" + }, + { + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "link" : "pygame.font.init", + "datetimeon" : "2011-03-10T18:43:35", + "id" : 3841 + }, + { + "id" : 3842, + "datetimeon" : "2011-03-10T18:43:37", + "link" : "pygame.font.init", + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous" + }, + { + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "link" : "pygame.font.init", + "datetimeon" : "2011-03-10T18:43:39", + "id" : 3843 + }, + { + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "link" : "pygame.font.init", + "datetimeon" : "2011-03-10T18:43:40", + "id" : 3844 + }, + { + "link" : "pygame.font.init", + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "id" : 3845, + "datetimeon" : "2011-03-10T18:43:42" + }, + { + "datetimeon" : "2011-03-10T18:43:45", + "id" : 3846, + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "link" : "pygame.font.init" + }, + { + "id" : 3847, + "datetimeon" : "2011-03-10T18:43:46", + "link" : "pygame.font.init", + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous" + }, + { + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "link" : "pygame.font.init", + "datetimeon" : "2011-03-10T18:43:48", + "id" : 3848 + }, + { + "id" : 3849, + "datetimeon" : "2011-03-10T18:43:50", + "link" : "pygame.font.init", + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous" + }, + { + "datetimeon" : "2011-03-10T18:43:52", + "id" : 3850, + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "link" : "pygame.font.init" + }, + { + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "link" : "pygame.font.init", + "datetimeon" : "2011-03-10T18:43:53", + "id" : 3851 + }, + { + "link" : "pygame.font.init", + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "id" : 3852, + "datetimeon" : "2011-03-10T18:43:55" + }, + { + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "link" : "pygame.font.init", + "datetimeon" : "2011-03-10T18:43:57", + "id" : 3853 + }, + { + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "link" : "pygame.font.init", + "datetimeon" : "2011-03-10T18:43:59", + "id" : 3854 + }, + { + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "link" : "pygame.font.init", + "datetimeon" : "2011-03-10T18:44:07", + "id" : 3855 + }, + { + "id" : 3856, + "datetimeon" : "2011-03-10T18:44:09", + "link" : "pygame.font.init", + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM" + }, + { + "link" : "pygame.font.init", + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "id" : 3857, + "datetimeon" : "2011-03-10T18:44:11" + }, + { + "datetimeon" : "2011-03-10T18:44:13", + "id" : 3858, + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "link" : "pygame.font.init" + }, + { + "link" : "pygame.font.init", + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "id" : 3859, + "datetimeon" : "2011-03-10T18:44:15" + }, + { + "id" : 3860, + "datetimeon" : "2011-03-10T18:44:18", + "link" : "pygame.font.init", + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM" + }, + { + "datetimeon" : "2011-03-10T18:44:20", + "id" : 3861, + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "link" : "pygame.font.init" + }, + { + "datetimeon" : "2011-03-10T18:44:22", + "id" : 3862, + "user_title" : "Anonymous", + "content" : "I LIKEY TO SPAM", + "link" : "pygame.font.init" + }, + { + "link" : "pygame.font.init", + "content" : "I LIKEY TO SPAM", + "user_title" : "Anonymous", + "id" : 3863, + "datetimeon" : "2011-03-10T18:44:24" + }, + { + "datetimeon" : "2011-03-12T11:25:09", + "id" : 3864, + "user_title" : "Anonymous", + "content" : "Hey guys, how do you detect if user hits enter? There's no event.key for that! Thank you.", + "link" : "pygame.key" + }, + { + "link" : "pygame.draw.circle", + "content" : "Could somebody please delete the horrible code in this comment thread? :(", + "user_title" : "Anonymous", + "id" : 3865, + "datetimeon" : "2011-03-15T20:16:46" + }, + { + "id" : 3868, + "datetimeon" : "2011-03-21T15:13:24", + "link" : "Font.metrics", + "user_title" : "Anonymous", + "content" : "pygame documentation\t || Pygame Home || Help Contents || Reference Index || \n \nCamera || Cdrom || Color || Cursors || Display || Draw || Event || Examples || Font || Gfxdraw || Image || Joystick || Key || Locals || Mask || Midi || Mixer || Mouse || Movie || Music || Overlay || Pixelarray || Pygame || Rect || Scrap || Sndarray || Sprite || Surface || Surfarray || Tests || Time || Transform\nFont.metrics\n\nThe user submitted comments should be used for:\n\nExamples\nHelpful hints, tips, and tricks\nFurther explanation / documentation\nThe user submitted comments should NOT be used for:\n\nBug Reports (see our new Bug Reports link on the side)\nFeature Requests\nQuestions\nPlease note that periodically, the developers may go through the notes and incorporate the information in them into the documentation. This means that any note submitted here becomes the property of Pete Shinners under the LGPL licence.\n\nIf you do not want to leave an anonymous comment, please sign in first." + }, + { + "id" : 3870, + "datetimeon" : "2011-03-22T11:32:04", + "link" : "pygame.transform.chop", + "content" : "I can't figure how to crop an image, even after reading this suggestion", + "user_title" : "Anonymous" + }, + { + "user_title" : "Anonymous", + "content" : "I was dumb. Here is how I got it to work. So simple:\n\ncreen.blit(gameboard,(selection.x-18,selection.y-18),(selection.x-18,selection.y-18,96,96))", + "link" : "pygame.transform.chop", + "datetimeon" : "2011-03-22T11:45:29", + "id" : 3871 + }, + { + "id" : 3873, + "datetimeon" : "2011-03-24T10:34:18", + "link" : "pygame.draw.circle", + "user_title" : "Anonymous", + "content" : "Perfect, Matthew Brown! Just what I was looking for. I found the reset_stuff() function especially useful and I'm going to use it in our production software." + }, + { + "user_title" : "Anonymous", + "content" : "Wow, thanks a lot Matthew, I am a PyGame newbie and was having some troubles understanding this function without a complete example.", + "link" : "pygame.draw.circle", + "datetimeon" : "2011-03-24T12:23:02", + "id" : 3874 + }, + { + "datetimeon" : "2011-03-24T18:06:29", + "id" : 3875, + "content" : "lol", + "user_title" : "Anonymous", + "link" : "pygame.draw.circle" + }, + { + "link" : "pygame.draw.circle", + "content" : "Guys, what Matthew N. Brown did here harms python's reputation.\n\nPython is elegant.\n\nWhat Matthew did was perlify python ... this is terrible...", + "user_title" : "Anonymous", + "id" : 3876, + "datetimeon" : "2011-03-24T18:07:11" + }, + { + "link" : "pygame.draw.circle", + "user_title" : "Anonymous", + "content" : "Just wanted to give my profuse thanks to Matthew N. Brown for his superb usage example of this otherwise vague and esoteric method. I've contacted my boss and we will now integrate this snippet into all of our newly created (and soon-to-be-refactored) legacy code.", + "id" : 3878, + "datetimeon" : "2011-03-25T20:41:38" + }, + { + "user_title" : "Anonymous", + "content" : "where image is a surface, rot and scale are floats\n\n return pygame.transform.smoothscale(image, rot, scale)\nTypeError: argument 2 must be 2-item sequence, not float\n \n return pygame.transform.smoothscale(image,[0,0], scale)\nTypeError: argument 3 must be pygame.Surface, not float", + "link" : "pygame.transform.rotozoom", + "datetimeon" : "2011-03-27T01:37:09", + "id" : 3879 + }, + { + "content" : "and...\n surf = pygame.surface.Surface((image.get_width()*scale, image.get_height()*scale))\n return pygame.transform.smoothscale(image,[0,0], surf)\n\n pygame.transform.smoothscale(image,[0,0], surf)\nValueError: Destination surface not the given width or height.", + "user_title" : "Anonymous", + "link" : "pygame.transform.rotozoom", + "datetimeon" : "2011-03-27T01:40:56", + "id" : 3880 + }, + { + "link" : "pygame.event.get_grab", + "user_title" : "Anonymous", + "content" : "look at set_grab first and then you understand", + "id" : 3881, + "datetimeon" : "2011-03-27T13:55:15" + }, + { + "link" : "Font.render", + "content" : "getting this error :\n in __init__\n self.font = pygame.font.Font(\"None\", 50)\nerror: font not initialized\n\nnot sure why because i rendered the font..", + "user_title" : "Anonymous", + "id" : 3882, + "datetimeon" : "2011-04-02T14:35:05" + }, + { + "datetimeon" : "2011-04-02T17:03:07", + "id" : 3883, + "content" : "The enter key is K_RETURN.", + "user_title" : "Anonymous", + "link" : "pygame.key" + }, + { + "content" : "For this error :\nthere is no soundcard\n\nCall pygame.mixer.init two times :\npygame.mixer.init()\npygame.mixer.init()\n\nOR\n\npygame.mixer.init(); pygame.mixer.init()\n\n(don't omit semicolon)", + "user_title" : "Anonymous", + "link" : "pygame.mixer.init", + "datetimeon" : "2011-04-05T05:06:41", + "id" : 3886 + }, + { + "datetimeon" : "2011-04-12T10:48:18", + "id" : 3889, + "user_title" : "Anonymous", + "content" : "/love little kids who leave dumb comments like this one", + "link" : "Rect.colliderect" + }, + { + "user_title" : "Anonymous", + "content" : "there is no information about the supported formats - are those from ModPlugTracker supported? (like .mod, .xm, .s3m, etc.)", + "link" : "pygame.mixer.music.load", + "datetimeon" : "2011-04-12T12:35:41", + "id" : 3890 + }, + { + "user_title" : "Anonymous", + "content" : "Yes", + "link" : "Font.render", + "datetimeon" : "2011-04-12T16:17:04", + "id" : 3891 + }, + { + "user_title" : "Anonymous", + "content" : "This seems to be broken!\nI call this before playing a movie, but there is no sound.\nPlaying the movie without initializing the mixer in the first place, works!\n\nCan anyone confirm that this is broken?", + "link" : "pygame.mixer.quit", + "datetimeon" : "2011-04-18T11:51:33", + "id" : 3895 + }, + { + "datetimeon" : "2011-04-19T10:47:45", + "id" : 3896, + "user_title" : "Anonymous", + "content" : "For Windows (XP):\nThe problem is that the screen does not get updated automatically, for some reason.\nThe solution is simple. Take a surface with the size of the movie, set this as the display of the movie.\nIn a loop, get the current frame of the movie and if it increases, blit the surface onto the screen and update the screen.", + "link" : "pygame.movie" + }, + { + "link" : "pygame.sndarray.make_sound", + "content" : "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\nfrom pygame import *\nimport pygame, time, numpy, pygame.sndarray\n\nsample_rate = 44100\n\ndef play_for(sample_array, ms, volLeft, volRight):\n sound = pygame.sndarray.make_sound(sample_array)\n beg = time.time()\n channel = sound.play(-1)\n channel.set_volume(volLeft,volRight)\n pygame.time.delay(ms)\n sound.stop()\n end = time.time()\n return beg, end\n \ndef sine_array_onecycle(hz, peak):\n length = sample_rate / float(hz)\n omega = numpy.pi * 2 / length\n xvalues = numpy.arange(int(length)) * omega\n return (peak * numpy.sin(xvalues))\n \ndef sine_array(hz, peak, n_samples = sample_rate):\n return numpy.resize(sine_array_onecycle(hz, peak), (n_samples,))\n \ndef main():\n pygame.mixer.pre_init(sample_rate, -16, 2) # 44.1kHz, 16-bit signed, stereo\n pygame.init()\n f = sine_array(8000, 1)\n f = numpy.array(zip (f , f))\n\n play_for(f , 5000, 0.5, 0.5)\n\nif __name__ == '__main__': main()", + "user_title" : "Anonymous", + "id" : 4038, + "datetimeon" : "2011-04-22T12:57:35" + }, + { + "content" : "Get Unicode key in Pygame:\n\nfrom pygame import *\npygame.init()\npygame.display.set_mode((500,500),OPENGLBLIT|OPENGL|DOUBLEBUF)\nexitt = 0 \ninte = 4096\nwhile not exitt:\n for event in pygame.event.get():\n if event.type == pygame.QUIT: \n pygame.quit()\n exitt = True\n if event.type == KEYDOWN:\n print event.dict['unicode']", + "user_title" : "Anonymous", + "link" : "pygame.key", + "datetimeon" : "2011-04-23T04:11:07", + "id" : 4039 + }, + { + "content" : "The param is \"loops\" not \"loop\".", + "user_title" : "Anonymous", + "link" : "Movie.play", + "datetimeon" : "2011-04-27T15:37:38", + "id" : 4040 + }, + { + "id" : 4041, + "datetimeon" : "2011-04-30T21:10:05", + "link" : "Surface.convert_alpha", + "user_title" : "Anonymous", + "content" : "Use it when you load PNG images with transparencies" + }, + { + "id" : 4042, + "datetimeon" : "2011-05-03T00:45:25", + "link" : "Surface.blit", + "user_title" : "Anonymous", + "content" : "The dest argument doesnt work for me, say if I do\n\nscreen.blit(mySurf, dest=(0,0))\n\nor screen.blit(mySurf, dest=(100,100))\n\nI get exactly the same outcome.\n\nWhere am i wrong?" + }, + { + "link" : "Surface.blit", + "user_title" : "Anonymous", + "content" : "Sorry I realized i was blitting to screen instead of my temporary surface,\nplease ignore (and delete) my comment.", + "id" : 4043, + "datetimeon" : "2011-05-03T00:46:43" + }, + { + "content" : "How does that differ from pygame.draw.aalines? This one can also not be filled.", + "user_title" : "Anonymous", + "link" : "pygame.gfxdraw.aapolygon", + "datetimeon" : "2011-05-04T14:58:27", + "id" : 4044 + }, + { + "content" : "XBM not supported?", + "user_title" : "Anonymous", + "link" : "pygame.image", + "datetimeon" : "2011-05-10T04:11:53", + "id" : 4048 + }, + { + "datetimeon" : "2011-05-11T00:48:47", + "id" : 4049, + "content" : "I believe it offsets the detection area by (x,y) pixels. So just put (0,0) for no offset", + "user_title" : "Anonymous", + "link" : "Mask.draw" + }, + { + "id" : 4050, + "datetimeon" : "2011-05-12T10:26:46", + "link" : "pygame.image.save", + "user_title" : "Anonymous", + "content" : "This function seems to need raw strings!" + }, + { + "id" : 4053, + "datetimeon" : "2011-05-14T16:30:13", + "link" : "pygame.mixer.music.load", + "user_title" : "Anonymous", + "content" : "Is there anyway to play more than 2 songs I have tried everything I want the loaded music to play in order\n#!/usr/bin/env python\nimport pygame\npygame.mixer.init()\npygame.mixer.pre_init(44100, -16, 2, 2048)\npygame.init()\nprint \"hey I finaly got this working!\"\npygame.mixer.music.load('D:/Users/John/Music/Music/FUN.OGG')\npygame.mixer.music.load('D:/Users/John/Music/Music/Still Alive.OGG')\npygame.mixer.music.load('D:/Users/John/Music/Music/turret.OGG')\npygame.mixer.music.load('D:/Users/John/Music/Music/portalend.OGG')\npygame.mixer.music.play()\nimport pysic" + }, + { + "id" : 4054, + "datetimeon" : "2011-05-14T19:29:16", + "link" : "pygame.mixer.music.play", + "content" : "How is it you play more than one song besides using queue", + "user_title" : "Anonymous" + }, + { + "content" : "how is it to play a list of songs more than just one without using the queue(which only works once)", + "user_title" : "Anonymous", + "link" : "pygame.mixer.music.play", + "datetimeon" : "2011-05-14T19:32:21", + "id" : 4055 + }, + { + "content" : "can the rectangle be filled with an RGBA color so that I can make it transclucent?", + "user_title" : "Anonymous", + "link" : "pygame.draw.rect", + "datetimeon" : "2011-05-26T09:16:14", + "id" : 4056 + }, + { + "user_title" : "Anonymous", + "content" : "Exemple, playing a sinus sound :\n\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom pygame import *\nimport pygame, time, numpy, pygame.sndarray\n\nsample_rate = 44100\n\ndef play_for(sample_array, ms, volLeft, volRight):\n sound = pygame.sndarray.make_sound(sample_array)\n beg = time.time()\n channel = sound.play(-1)\n channel.set_volume(volLeft,volRight)\n pygame.time.delay(ms)\n sound.stop()\n end = time.time()\n return beg, end\n \ndef sine_array_onecycle(hz, peak):\n length = sample_rate / float(hz)\n omega = numpy.pi * 2 / length\n xvalues = numpy.arange(int(length)) * omega\n return (peak * numpy.sin(xvalues))\n \ndef sine_array(hz, peak, n_samples = sample_rate):\n return numpy.resize(sine_array_onecycle(hz, peak), (n_samples,))\n\n \ndef main():\n pygame.mixer.pre_init(sample_rate, -16, 2) # 44.1kHz, 16-bit signed, stereo\n pygame.init()\n f = sine_array(8000, 1)\n f = numpy.array(zip (f , f))\n\n play_for(f , 5000, 0.2, 0.2)\n\nif __name__ == '__main__': main()", + "link" : "pygame.sndarray", + "datetimeon" : "2011-05-27T02:36:07", + "id" : 4057 + } +] diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/draw.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/draw.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..a425398 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/draw.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/draw.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/draw.pyi new file mode 100644 index 0000000..9aeeae9 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/draw.pyi @@ -0,0 +1,83 @@ +from typing import Union, Optional, Tuple, List, Sequence +from pygame.color import Color +from pygame.rect import Rect +from pygame.surface import Surface +from pygame.math import Vector2 + +_Coordinate = Union[Tuple[float, float], List[float], Vector2] +_ColorValue = Union[ + Color, Tuple[int, int, int], List[int], int, Tuple[int, int, int, int] +] +_RectValue = Union[ + Rect, + Union[Tuple[int, int, int, int], List[int]], + Union[Tuple[_Coordinate, _Coordinate], List[_Coordinate]], +] + +def rect( + surface: Surface, + color: _ColorValue, + rect: _RectValue, + width: Optional[int] = 0, + border_radius: Optional[int] = -1, + border_top_left_radius: Optional[int] = -1, + border_top_right_radius: Optional[int] = -1, + border_bottom_left_radius: Optional[int] = -1, + border_bottom_right_radius: Optional[int] = -1, +) -> Rect: ... +def polygon( + surface: Surface, + color: _ColorValue, + points: Sequence[_Coordinate], + width: Optional[int] = 0, +) -> Rect: ... +def circle( + surface: Surface, + color: _ColorValue, + center: _Coordinate, + radius: float, + width: Optional[int] = 0, + draw_top_right: Optional[bool] = None, + draw_top_left: Optional[bool] = None, + draw_bottom_left: Optional[bool] = None, + draw_bottom_right: Optional[bool] = None, +) -> Rect: ... +def ellipse( + surface: Surface, color: _ColorValue, rect: _RectValue, width: Optional[int] = 0 +) -> Rect: ... +def arc( + surface: Surface, + color: _ColorValue, + rect: _RectValue, + start_angle: float, + stop_angle: float, + width: Optional[int] = 1, +) -> Rect: ... +def line( + surface: Surface, + color: _ColorValue, + start_pos: _Coordinate, + end_pos: _Coordinate, + width: Optional[int] = 1, +) -> Rect: ... +def lines( + surface: Surface, + color: _ColorValue, + closed: bool, + points: Sequence[_Coordinate], + width: Optional[int] = 1, +) -> Rect: ... +def aaline( + surface: Surface, + color: _ColorValue, + start_pos: _Coordinate, + end_pos: _Coordinate, + blend: Optional[int] = 1, +) -> Rect: ... +def aalines( + surface: Surface, + color: _ColorValue, + closed: bool, + points: Sequence[_Coordinate], + blend: Optional[int] = 1, +) -> Rect: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/draw_py.py b/Display/.venv/lib/python3.7/site-packages/pygame/draw_py.py new file mode 100644 index 0000000..9d04e1b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/draw_py.py @@ -0,0 +1,540 @@ +# -*- coding: utf-8 -*- +'''Pygame Drawing algorithms written in Python. (Work in Progress) + +Implement Pygame's Drawing Algorithms in a Python version for testing +and debugging. +''' +from __future__ import absolute_import, division +import sys + +if sys.version_info >= (3, 0, 0): + from math import floor, ceil +else: + # Python2.7 + # FIXME : the import of the builtin math module is broken ... + def floor(x): + int_x = int(x) + return int_x if (x == int_x or x > 0) else int_x - 1 + + def ceil(x): + int_x = int(x) + return int_x if (int_x == x or x < 0) else int_x + 1 + + +# H E L P E R F U N C T I O N S # + +# fractional part of x + +def frac(x): + '''return fractional part of x''' + return x - floor(x) + +def inv_frac(x): + '''return inverse fractional part of x''' + return 1 - (x - floor(x)) # eg, 1 - frac(x) + + +# L O W L E V E L D R A W F U N C T I O N S # +# (They are too low-level to be translated into python, right?) + +def set_at(surf, x, y, color): + surf.set_at((x, y), color) + + +def draw_pixel(surf, x, y, color, bright, blend=True): + '''draw one blended pixel with given brightness.''' + try: + other_col = surf.get_at((x, y)) if blend else (0, 0, 0, 0) + except IndexError: # pixel outside the surface + return + new_color = tuple((bright * col + (1 - bright) * pix) + for col, pix in zip(color, other_col)) + # FIXME what should happen if only one, color or surf_col, has alpha? + surf.set_at((x, y), new_color) + + +def _drawhorzline(surf, color, x_from, y, x_to): + if x_from == x_to: + surf.set_at((x_from, y), color) + return + + start, end = (x_from, x_to) if x_from <= x_to else (x_to, x_from) + for x in range(start, end + 1): + surf.set_at((x, y), color) + + +def _drawvertline(surf, color, x, y_from, y_to): + if y_from == y_to: + surf.set_at((x, y_from), color) + return + + start, end = (y_from, y_to) if y_from <= y_to else (y_to, y_from) + for y in range(start, end + 1): + surf.set_at((x, y), color) + + +# I N T E R N A L D R A W L I N E F U N C T I O N S # + +def _clip_and_draw_horzline(surf, color, x_from, y, x_to): + '''draw clipped horizontal line.''' + # check Y inside surf + clip = surf.get_clip() + if y < clip.y or y >= clip.y + clip.h: + return + + x_from = max(x_from, clip.x) + x_to = min(x_to, clip.x + clip.w - 1) + + # check any x inside surf + if x_to < clip.x or x_from >= clip.x + clip.w: + return + + _drawhorzline(surf, color, x_from, y, x_to) + + +def _clip_and_draw_vertline(surf, color, x, y_from, y_to): + '''draw clipped vertical line.''' + # check X inside surf + clip = surf.get_clip() + + if x < clip.x or x >= clip.x + clip.w: + return + + y_from = max(y_from, clip.y) + y_to = min(y_to, clip.y + clip.h - 1) + + # check any y inside surf + if y_to < clip.y or y_from >= clip.y + clip.h: + return + + _drawvertline(surf, color, x, y_from, y_to) + +# These constans xxx_EDGE are "outside-the-bounding-box"-flags +LEFT_EDGE = 0x1 +RIGHT_EDGE = 0x2 +BOTTOM_EDGE = 0x4 +TOP_EDGE = 0x8 + +def encode(x, y, left, top, right, bottom): + '''returns a code that defines position with respect to a bounding box''' + # we use the fact that python interprets booleans (the inqualities) + # as 0/1, and then multiply them with the xxx_EDGE flags + return ((x < left) * LEFT_EDGE + + (x > right) * RIGHT_EDGE + + (y < top) * TOP_EDGE + + (y > bottom) * BOTTOM_EDGE) + + +INSIDE = lambda a: not a +ACCEPT = lambda a, b: not (a or b) +REJECT = lambda a, b: a and b + + +def clip_line(line, left, top, right, bottom, use_float=False): + '''Algorithm to calculate the clipped line. + + We calculate the coordinates of the part of the line segment within the + bounding box (defined by left, top, right, bottom). The we write + the coordinates of the line segment into "line", much like the C-algorithm. + With `use_float` True, clip_line is usable for float-clipping. + + Returns: true if the line segment cuts the bounding box (false otherwise) + ''' + assert isinstance(line, list) + x1, y1, x2, y2 = line + dtype = float if use_float else int + + while True: + # the coordinates are progressively modified with the codes, + # until they are either rejected or correspond to the final result. + code1 = encode(x1, y1, left, top, right, bottom) + code2 = encode(x2, y2, left, top, right, bottom) + + if ACCEPT(code1, code2): + # write coordinates into "line" ! + line[:] = x1, y1, x2, y2 + return True + if REJECT(code1, code2): + return False + + # We operate on the (x1, y1) point, and swap if it is inside the bbox: + if INSIDE(code1): + x1, x2 = x2, x1 + y1, y2 = y2, y1 + code1, code2 = code2, code1 + if (x2 != x1): + m = (y2 - y1) / float(x2 - x1) + else: + m = 1.0 + # Each case, if true, means that we are outside the border: + # calculate x1 and y1 to be the "first point" inside the bbox... + if code1 & LEFT_EDGE: + y1 += dtype((left - x1) * m) + x1 = left + elif code1 & RIGHT_EDGE: + y1 += dtype((right - x1) * m) + x1 = right + elif code1 & BOTTOM_EDGE: + if x2 != x1: + x1 += dtype((bottom - y1) / m) + y1 = bottom + elif code1 & TOP_EDGE: + if x2 != x1: + x1 += dtype((top - y1) / m) + y1 = top + + +def _draw_line(surf, color, x1, y1, x2, y2): + '''draw a non-horizontal line (without anti-aliasing).''' + # Variant of https://en.wikipedia.org/wiki/Bresenham's_line_algorithm + # + # This strongly differs from craw.c implementation, because we use a + # "slope" variable (instead of delta_x and delta_y) and a "error" variable. + # And we can not do pointer-arithmetic with "BytesPerPixel", like in + # the C-algorithm. + if x1 == x2: + # This case should not happen... + raise ValueError + + slope = abs((y2 - y1) / (x2 - x1)) + error = 0.0 + + if slope < 1: + # Here, it's a rather horizontal line + + # 1. check in which octants we are & set init values + if x2 < x1: + x1, x2 = x2, x1 + y1, y2 = y2, y1 + y = y1 + dy_sign = 1 if (y1 < y2) else -1 + + # 2. step along x coordinate + for x in range(x1, x2 + 1): + set_at(surf, x, y, color) + error += slope + if error >= 0.5: + y += dy_sign + error -= 1 + else: + # Case of a rather vertical line + + # 1. check in which octants we are & set init values + if y1 > y2: + x1, x2 = x2, x1 + y1, y2 = y2, y1 + x = x1 + slope = 1 / slope + dx_sign = 1 if (x1 < x2) else -1 + + # 2. step along y coordinate + for y in range(y1, y2 + 1): + set_at(surf, x, y, color) + error += slope + if error >= 0.5: + x += dx_sign + error -= 1 + + +def _draw_aaline(surf, color, from_x, from_y, to_x, to_y, blend): + '''draw an anti-aliased line. + + The algorithm yields identical results with _draw_line for horizontal, + vertical or diagonal lines, and results changes smoothly when changing + any of the endpoint coordinates. + + Note that this yields strange results for very short lines, eg + a line from (0, 0) to (0, 1) will draw 2 pixels, and a line from + (0, 0) to (0, 1.1) will blend 10 % on the pixel (0, 2). + ''' + # The different requirements that we have on an antialiasing algorithm + # implies to make some compromises: + # 1. We want smooth evolution wrt to the 4 endpoint coordinates + # (this means also that we want a smooth evolution when the angle + # passes +/- 45° + # 2. We want the same behavior when swapping the endpoints + # 3. We want understandable results for the endpoint values + # (eg we want to avoid half-integer values to draw a simple plain + # horizontal or vertical line between two integer l endpoints) + # + # This implies to somehow make the line artificially 1 pixel longer + # and to draw a full pixel when we have the endpoints are identical. + dx = to_x - from_x + dy = to_y - from_y + + if dx == 0 and dy == 0: + # For smoothness reasons, we could also do some blending here, + # but it seems overshoot... + set_at(surf, int(from_x), int(from_y), color) + return + + if abs(dx) >= abs(dy): + if from_x > to_x: + from_x, to_x = to_x, from_x + from_y, to_y = to_y, from_y + dx = -dx + dy = -dy + + slope = dy / dx + def draw_two_pixel(x, float_y, factor): + y = floor(float_y) + draw_pixel(surf, x, y, color, factor * inv_frac(float_y), blend) + draw_pixel(surf, x, y + 1, color, factor * frac(float_y), blend) + + # A and G are respectively left and right to the "from" point, but + # with integer-x-coordinate, (and only if from_x is not integer). + # Hence they appear in following order on the line in general case: + # A from-pt G . . . to-pt S + # |------*-------|--- . . . ---|-----*------|- + G_x = ceil(from_x) + G_y = from_y + (G_x - from_x) * slope + + # 1. Draw start of the segment if we have a non-integer-part + if from_x < G_x: + # this corresponds to the point "A" + draw_two_pixel(floor(from_x), G_y - slope, inv_frac(from_x)) + + # 2. Draw end of the segment: we add one pixel for homogenity reasons + rest = frac(to_x) + S_x = ceil(to_x) + if rest > 0: + # Again we draw only if we have a non-integer-part + S_y = from_y + slope * (dx + 1 - rest) + draw_two_pixel(S_x, S_y, rest) + else: + S_x += 1 + + # 3. loop for other points + for x in range(G_x, S_x): + y = G_y + slope * (x - G_x) + draw_two_pixel(x, y, 1) + + else: + if from_y > to_y: + from_x, to_x = to_x, from_x + from_y, to_y = to_y, from_y + dx = -dx + dy = -dy + + slope = dx / dy + + def draw_two_pixel(float_x, y, factor): + x = floor(float_x) + draw_pixel(surf, x, y, color, factor * inv_frac(float_x), blend) + draw_pixel(surf, x + 1, y, color, factor * frac(float_x), blend) + + G_y = ceil(from_y) + G_x = from_x + (G_y - from_y) * slope + + # 1. Draw start of the segment + if from_y < G_y: + draw_two_pixel(G_x - slope, floor(from_y), inv_frac(from_y)) + + # 2. Draw end of the segment + rest = frac(to_y) + S_y = ceil(to_y) + if rest > 0: + S_x = from_x + slope * (dy + 1 - rest) + draw_two_pixel(S_x, S_y, rest) + else: + S_y += 1 + + # 3. loop for other points + for y in range(G_y, S_y): + x = G_x + slope * (y - G_y) + draw_two_pixel(x, y, 1) + + +# C L I P A N D D R A W L I N E F U N C T I O N S # + +def _clip_and_draw_line(surf, rect, color, pts): + '''clip the line into the rectangle and draw if needed. + + Returns true if anything has been drawn, else false.''' + # "pts" is a list with the four coordinates of the two endpoints + # of the line to be drawn : pts = x1, y1, x2, y2. + # The data format is like that to stay closer to the C-algorithm. + if not clip_line(pts, rect.x, rect.y, rect.x + rect.w - 1, + rect.y + rect.h - 1): + # The line segment defined by "pts" is not crossing the rectangle + return 0 + if pts[1] == pts[3]: # eg y1 == y2 + _drawhorzline(surf, color, pts[0], pts[1], pts[2]) + elif pts[0] == pts[2]: # eg x1 == x2 + _drawvertline(surf, color, pts[0], pts[1], pts[3]) + else: + _draw_line(surf, color, pts[0], pts[1], pts[2], pts[3]) + return 1 + +def _clip_and_draw_line_width(surf, rect, color, line, width): + yinc = xinc = 0 + if abs(line[0] - line[2]) > abs(line[1] - line[3]): + yinc = 1 + else: + xinc = 1 + newpts = line[:] + if _clip_and_draw_line(surf, rect, color, newpts): + anydrawn = 1 + frame = newpts[:] + else: + anydrawn = 0 + frame = [10000, 10000, -10000, -10000] + + for loop in range(1, width // 2 + 1): + newpts[0] = line[0] + xinc * loop + newpts[1] = line[1] + yinc * loop + newpts[2] = line[2] + xinc * loop + newpts[3] = line[3] + yinc * loop + if _clip_and_draw_line(surf, rect, color, newpts): + anydrawn = 1 + frame[0] = min(newpts[0], frame[0]) + frame[1] = min(newpts[1], frame[1]) + frame[2] = max(newpts[2], frame[2]) + frame[3] = max(newpts[3], frame[3]) + + if loop * 2 < width: + newpts[0] = line[0] - xinc * loop + newpts[1] = line[1] - yinc * loop + newpts[2] = line[2] - xinc * loop + newpts[3] = line[3] - yinc * loop + if _clip_and_draw_line(surf, rect, color, newpts): + anydrawn = 1 + frame[0] = min(newpts[0], frame[0]) + frame[1] = min(newpts[1], frame[1]) + frame[2] = max(newpts[2], frame[2]) + frame[3] = max(newpts[3], frame[3]) + + return anydrawn + + +def _clip_and_draw_aaline(surf, rect, color, line, blend): + '''draw anti-aliased line between two endpoints.''' + if not clip_line(line, rect.x - 1, rect.y -1, rect.x + rect.w, + rect.y + rect.h, use_float=True): + return # TODO Rect(rect.x, rect.y, 0, 0) + _draw_aaline(surf, color, line[0], line[1], line[2], line[3], blend) + return # TODO Rect(-- affected area --) + + +# D R A W L I N E F U N C T I O N S # + +def draw_aaline(surf, color, from_point, to_point, blend=True): + '''draw anti-aliased line between two endpoints.''' + line = [from_point[0], from_point[1], to_point[0], to_point[1]] + return _clip_and_draw_aaline(surf, surf.get_clip(), color, line, blend) + + +def draw_line(surf, color, from_point, to_point, width=1): + '''draw anti-aliased line between two endpoints.''' + line = [from_point[0], from_point[1], to_point[0], to_point[1]] + return _clip_and_draw_line_width(surf, surf.get_clip(), color, line, width) + + +# M U L T I L I N E F U N C T I O N S # + +def _multi_lines(surf, color, closed, points, width=1, blend=False, aaline=False): + '''draw several lines, either anti-aliased or not.''' + # The code for anti-aliased or not is almost identical, so it's factorized + length = len(points) + if length <= 2: + raise TypeError + line = [0] * 4 # store x1, y1 & x2, y2 of the lines to be drawn + + xlist = [pt[0] for pt in points] + ylist = [pt[1] for pt in points] + left = right = line[0] = xlist[0] + top = bottom = line[1] = ylist[0] + + for x, y in points[1:]: + left = min(left, x) + right = max(right, x) + top = min(top, y) + bottom = max(bottom, x) + + rect = surf.get_clip() + for loop in range(1, length): + + line[0] = xlist[loop - 1] + line[1] = ylist[loop - 1] + line[2] = xlist[loop] + line[3] = ylist[loop] + if aaline: + _clip_and_draw_aaline(surf, rect, color, line, blend) + else: + _clip_and_draw_line_width(surf, rect, color, line, width) + + if closed: + line[0] = xlist[length - 1] + line[1] = ylist[length - 1] + line[2] = xlist[0] + line[3] = ylist[0] + if aaline: + _clip_and_draw_aaline(surf, rect, color, line, blend) + else: + _clip_and_draw_line_width(surf, rect, color, line, width) + + return # TODO Rect(...) + +def draw_lines(surf, color, closed, points, width=1): + '''draw several lines connected through the points.''' + return _multi_lines(surf, color, closed, points, width, aaline=False) + + +def draw_aalines(surf, color, closed, points, blend=True): + '''draw several anti-aliased lines connected through the points.''' + return _multi_lines(surf, color, closed, points, blend=blend, aaline=True) + + +def draw_polygon(surface, color, points, width): + if width: + draw_lines(surface, color, 1, points, width) + return # TODO Rect(...) + num_points = len(points) + point_x = [x for x, y in points] + point_y = [y for x, y in points] + + miny = min(point_y) + maxy = max(point_y) + + if miny == maxy: + minx = min(point_x) + maxx = max(point_x) + _clip_and_draw_horzline(surface, color, minx, miny, maxx) + return # TODO Rect(...) + + for y in range(miny, maxy + 1): + x_intersect = [] + for i in range(num_points): + i_prev = i - 1 if i else num_points - 1 + + y1 = point_y[i_prev] + y2 = point_y[i] + + if y1 < y2: + x1 = point_x[i_prev] + x2 = point_x[i] + elif y1 > y2: + y2 = point_y[i_prev] + y1 = point_y[i] + x2 = point_x[i_prev] + x1 = point_x[i] + else: # special case handled below + continue + + if ( ((y >= y1) and (y < y2)) or ((y == maxy) and (y <= y2))) : + x_sect = (y - y1) * (x2 - x1) // (y2 - y1) + x1 + x_intersect.append(x_sect) + + x_intersect.sort() + for i in range(0, len(x_intersect), 2): + _clip_and_draw_horzline(surface, color, x_intersect[i], y, + x_intersect[i + 1]) + + # special case : horizontal border lines + for i in range(num_points): + i_prev = i - 1 if i else num_points - 1 + y = point_y[i] + if miny < y == point_y[i_prev] < maxy: + _clip_and_draw_horzline(surface, color, point_x[i], y, point_x[i_prev]) + + return # TODO Rect(...) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/event.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/event.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..7ec4494 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/event.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/event.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/event.pyi new file mode 100644 index 0000000..737856f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/event.pyi @@ -0,0 +1,31 @@ +from typing import Any, Dict, List, Optional, Union, overload + +class EventType(object): + type: int + __dict__: Dict[str, Any] + @overload + def __init__(self, type: int, dict: Dict[str, Any]) -> None: ... + @overload + def __init__(self, type: int, **attributes: Any) -> None: ... + def __getattr__(self, name: str) -> Any: ... + +# Event is actually a function that returns an EventType, but it's often used +# as an annotation. +Event = EventType + +_EventIds = Union[int, List[int]] + +def pump() -> None: ... +def get(type: _EventIds = ...) -> List[EventType]: ... +def poll() -> EventType: ... +def wait() -> EventType: ... +def peak(type: _EventIds) -> bool: ... +def clear(type: _EventIds = ...) -> None: ... +def event_name(type: int) -> str: ... +def set_blocked(type: Optional[_EventIds]) -> None: ... +def set_allowed(type: Optional[_EventIds]) -> None: ... +def get_blocked(type: int) -> bool: ... +def set_grab(grab: bool) -> None: ... +def get_grab() -> bool: ... +def post(event: EventType) -> None: ... +def custom_type() -> int: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/aacircle.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/aacircle.py new file mode 100644 index 0000000..067334b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/aacircle.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +"""Proof of concept gfxdraw example""" + +import pygame +import pygame.gfxdraw + + +def main(): + pygame.init() + screen = pygame.display.set_mode((500, 500)) + screen.fill((255, 0, 0)) + s = pygame.Surface(screen.get_size(), pygame.SRCALPHA, 32) + pygame.draw.line(s, (0, 0, 0), (250, 250), (250 + 200, 250)) + + width = 1 + for a_radius in range(width): + radius = 200 + pygame.gfxdraw.aacircle(s, 250, 250, radius - a_radius, (0, 0, 0)) + + screen.blit(s, (0, 0)) + + pygame.draw.circle(screen, pygame.Color("GREEN"), (50, 100), 10) + pygame.draw.circle(screen, pygame.Color("BLACK"), (50, 100), 10, 1) + + pygame.display.flip() + try: + while 1: + event = pygame.event.wait() + if event.type == pygame.QUIT: + break + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_ESCAPE or event.unicode == "q": + break + pygame.display.flip() + finally: + pygame.quit() + + +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/aliens.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/aliens.py new file mode 100644 index 0000000..8e72139 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/aliens.py @@ -0,0 +1,410 @@ +#!/usr/bin/env python +""" pygame.examples.aliens + +Shows a mini game where you have to defend against aliens. + +What does it show you about pygame? + +* pg.sprite, the difference between Sprite and Group. +* dirty rectangle optimization for processing for speed. +* music with pg.mixer.music, including fadeout +* sound effects with pg.Sound +* event processing, keyboard handling, QUIT handling. +* a main loop frame limited with a game clock from pg.time.Clock +* fullscreen switching. + + +Controls +-------- + +* Left and right arrows to move. +* Space bar to shoot +* f key to toggle between fullscreen. + +""" + +import random +import os + +# import basic pygame modules +import pygame as pg + +# see if we can load more than standard BMP +if not pg.image.get_extended(): + raise SystemExit("Sorry, extended image module required") + + +# game constants +MAX_SHOTS = 2 # most player bullets onscreen +ALIEN_ODDS = 22 # chances a new alien appears +BOMB_ODDS = 60 # chances a new bomb will drop +ALIEN_RELOAD = 12 # frames between new aliens +SCREENRECT = pg.Rect(0, 0, 640, 480) +SCORE = 0 + +main_dir = os.path.split(os.path.abspath(__file__))[0] + + +def load_image(file): + """ loads an image, prepares it for play + """ + file = os.path.join(main_dir, "data", file) + try: + surface = pg.image.load(file) + except pg.error: + raise SystemExit('Could not load image "%s" %s' % (file, pg.get_error())) + return surface.convert() + + +def load_sound(file): + """ because pygame can be be compiled without mixer. + """ + if not pg.mixer: + return None + file = os.path.join(main_dir, "data", file) + try: + sound = pg.mixer.Sound(file) + return sound + except pg.error: + print("Warning, unable to load, %s" % file) + return None + + +# Each type of game object gets an init and an update function. +# The update function is called once per frame, and it is when each object should +# change it's current position and state. +# +# The Player object actually gets a "move" function instead of update, +# since it is passed extra information about the keyboard. + + +class Player(pg.sprite.Sprite): + """ Representing the player as a moon buggy type car. + """ + + speed = 10 + bounce = 24 + gun_offset = -11 + images = [] + + def __init__(self): + pg.sprite.Sprite.__init__(self, self.containers) + self.image = self.images[0] + self.rect = self.image.get_rect(midbottom=SCREENRECT.midbottom) + self.reloading = 0 + self.origtop = self.rect.top + self.facing = -1 + + def move(self, direction): + if direction: + self.facing = direction + self.rect.move_ip(direction * self.speed, 0) + self.rect = self.rect.clamp(SCREENRECT) + if direction < 0: + self.image = self.images[0] + elif direction > 0: + self.image = self.images[1] + self.rect.top = self.origtop - (self.rect.left // self.bounce % 2) + + def gunpos(self): + pos = self.facing * self.gun_offset + self.rect.centerx + return pos, self.rect.top + + +class Alien(pg.sprite.Sprite): + """ An alien space ship. That slowly moves down the screen. + """ + + speed = 13 + animcycle = 12 + images = [] + + def __init__(self): + pg.sprite.Sprite.__init__(self, self.containers) + self.image = self.images[0] + self.rect = self.image.get_rect() + self.facing = random.choice((-1, 1)) * Alien.speed + self.frame = 0 + if self.facing < 0: + self.rect.right = SCREENRECT.right + + def update(self): + self.rect.move_ip(self.facing, 0) + if not SCREENRECT.contains(self.rect): + self.facing = -self.facing + self.rect.top = self.rect.bottom + 1 + self.rect = self.rect.clamp(SCREENRECT) + self.frame = self.frame + 1 + self.image = self.images[self.frame // self.animcycle % 3] + + +class Explosion(pg.sprite.Sprite): + """ An explosion. Hopefully the Alien and not the player! + """ + + defaultlife = 12 + animcycle = 3 + images = [] + + def __init__(self, actor): + pg.sprite.Sprite.__init__(self, self.containers) + self.image = self.images[0] + self.rect = self.image.get_rect(center=actor.rect.center) + self.life = self.defaultlife + + def update(self): + """ called every time around the game loop. + + Show the explosion surface for 'defaultlife'. + Every game tick(update), we decrease the 'life'. + + Also we animate the explosion. + """ + self.life = self.life - 1 + self.image = self.images[self.life // self.animcycle % 2] + if self.life <= 0: + self.kill() + + +class Shot(pg.sprite.Sprite): + """ a bullet the Player sprite fires. + """ + + speed = -11 + images = [] + + def __init__(self, pos): + pg.sprite.Sprite.__init__(self, self.containers) + self.image = self.images[0] + self.rect = self.image.get_rect(midbottom=pos) + + def update(self): + """ called every time around the game loop. + + Every tick we move the shot upwards. + """ + self.rect.move_ip(0, self.speed) + if self.rect.top <= 0: + self.kill() + + +class Bomb(pg.sprite.Sprite): + """ A bomb the aliens drop. + """ + + speed = 9 + images = [] + + def __init__(self, alien): + pg.sprite.Sprite.__init__(self, self.containers) + self.image = self.images[0] + self.rect = self.image.get_rect(midbottom=alien.rect.move(0, 5).midbottom) + + def update(self): + """ called every time around the game loop. + + Every frame we move the sprite 'rect' down. + When it reaches the bottom we: + + - make an explosion. + - remove the Bomb. + """ + self.rect.move_ip(0, self.speed) + if self.rect.bottom >= 470: + Explosion(self) + self.kill() + + +class Score(pg.sprite.Sprite): + """ to keep track of the score. + """ + + def __init__(self): + pg.sprite.Sprite.__init__(self) + self.font = pg.font.Font(None, 20) + self.font.set_italic(1) + self.color = pg.Color("white") + self.lastscore = -1 + self.update() + self.rect = self.image.get_rect().move(10, 450) + + def update(self): + """ We only update the score in update() when it has changed. + """ + if SCORE != self.lastscore: + self.lastscore = SCORE + msg = "Score: %d" % SCORE + self.image = self.font.render(msg, 0, self.color) + + +def main(winstyle=0): + # Initialize pygame + if pg.get_sdl_version()[0] == 2: + pg.mixer.pre_init(44100, 32, 2, 1024) + pg.init() + if pg.mixer and not pg.mixer.get_init(): + print("Warning, no sound") + pg.mixer = None + + fullscreen = False + # Set the display mode + winstyle = 0 # |FULLSCREEN + bestdepth = pg.display.mode_ok(SCREENRECT.size, winstyle, 32) + screen = pg.display.set_mode(SCREENRECT.size, winstyle, bestdepth) + + # Load images, assign to sprite classes + # (do this before the classes are used, after screen setup) + img = load_image("player1.gif") + Player.images = [img, pg.transform.flip(img, 1, 0)] + img = load_image("explosion1.gif") + Explosion.images = [img, pg.transform.flip(img, 1, 1)] + Alien.images = [load_image(im) for im in ("alien1.gif", "alien2.gif", "alien3.gif")] + Bomb.images = [load_image("bomb.gif")] + Shot.images = [load_image("shot.gif")] + + # decorate the game window + icon = pg.transform.scale(Alien.images[0], (32, 32)) + pg.display.set_icon(icon) + pg.display.set_caption("Pygame Aliens") + pg.mouse.set_visible(0) + + # create the background, tile the bgd image + bgdtile = load_image("background.gif") + background = pg.Surface(SCREENRECT.size) + for x in range(0, SCREENRECT.width, bgdtile.get_width()): + background.blit(bgdtile, (x, 0)) + screen.blit(background, (0, 0)) + pg.display.flip() + + # load the sound effects + boom_sound = load_sound("boom.wav") + shoot_sound = load_sound("car_door.wav") + if pg.mixer: + music = os.path.join(main_dir, "data", "house_lo.wav") + pg.mixer.music.load(music) + pg.mixer.music.play(-1) + + # Initialize Game Groups + aliens = pg.sprite.Group() + shots = pg.sprite.Group() + bombs = pg.sprite.Group() + all = pg.sprite.RenderUpdates() + lastalien = pg.sprite.GroupSingle() + + # assign default groups to each sprite class + Player.containers = all + Alien.containers = aliens, all, lastalien + Shot.containers = shots, all + Bomb.containers = bombs, all + Explosion.containers = all + Score.containers = all + + # Create Some Starting Values + global score + alienreload = ALIEN_RELOAD + clock = pg.time.Clock() + + # initialize our starting sprites + global SCORE + player = Player() + Alien() # note, this 'lives' because it goes into a sprite group + if pg.font: + all.add(Score()) + + # Run our main loop whilst the player is alive. + while player.alive(): + + # get input + for event in pg.event.get(): + if event.type == pg.QUIT: + return + if event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE: + return + elif event.type == pg.KEYDOWN: + if event.key == pg.K_f: + if not fullscreen: + print("Changing to FULLSCREEN") + screen_backup = screen.copy() + screen = pg.display.set_mode( + SCREENRECT.size, winstyle | pg.FULLSCREEN, bestdepth + ) + screen.blit(screen_backup, (0, 0)) + else: + print("Changing to windowed mode") + screen_backup = screen.copy() + screen = pg.display.set_mode( + SCREENRECT.size, winstyle, bestdepth + ) + screen.blit(screen_backup, (0, 0)) + pg.display.flip() + fullscreen = not fullscreen + + keystate = pg.key.get_pressed() + + # clear/erase the last drawn sprites + all.clear(screen, background) + + # update all the sprites + all.update() + + # handle player input + direction = keystate[pg.K_RIGHT] - keystate[pg.K_LEFT] + player.move(direction) + firing = keystate[pg.K_SPACE] + if not player.reloading and firing and len(shots) < MAX_SHOTS: + Shot(player.gunpos()) + if pg.mixer: + shoot_sound.play() + player.reloading = firing + + # Create new alien + if alienreload: + alienreload = alienreload - 1 + elif not int(random.random() * ALIEN_ODDS): + Alien() + alienreload = ALIEN_RELOAD + + # Drop bombs + if lastalien and not int(random.random() * BOMB_ODDS): + Bomb(lastalien.sprite) + + # Detect collisions between aliens and players. + for alien in pg.sprite.spritecollide(player, aliens, 1): + if pg.mixer: + boom_sound.play() + Explosion(alien) + Explosion(player) + SCORE = SCORE + 1 + player.kill() + + # See if shots hit the aliens. + for alien in pg.sprite.groupcollide(shots, aliens, 1, 1).keys(): + if pg.mixer: + boom_sound.play() + Explosion(alien) + SCORE = SCORE + 1 + + # See if alien boms hit the player. + for bomb in pg.sprite.spritecollide(player, bombs, 1): + if pg.mixer: + boom_sound.play() + Explosion(player) + Explosion(bomb) + player.kill() + + # draw the scene + dirty = all.draw(screen) + pg.display.update(dirty) + + # cap the framerate at 40fps. Also called 40HZ or 40 times per second. + clock.tick(40) + + if pg.mixer: + pg.mixer.music.fadeout(1000) + pg.time.wait(1000) + pg.quit() + + +# call the "main" function if running this script +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/arraydemo.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/arraydemo.py new file mode 100644 index 0000000..998fd2e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/arraydemo.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +""" pygame.examples.arraydemo + +Welcome to the arraydemo! + +Use the numpy array package to manipulate pixels. + +This demo will show you a few things: + +* scale up, scale down, flip, +* cross fade +* soften +* put stripes on it! + +""" + + +import os + +import pygame as pg +from pygame import surfarray + +main_dir = os.path.split(os.path.abspath(__file__))[0] + + +def surfdemo_show(array_img, name): + "displays a surface, waits for user to continue" + screen = pg.display.set_mode(array_img.shape[:2], 0, 32) + surfarray.blit_array(screen, array_img) + pg.display.flip() + pg.display.set_caption(name) + while 1: + e = pg.event.wait() + if e.type == pg.MOUSEBUTTONDOWN: + break + elif e.type == pg.KEYDOWN and e.key == pg.K_s: + # pg.image.save(screen, name+'.bmp') + # s = pg.Surface(screen.get_size(), 0, 32) + # s = s.convert_alpha() + # s.fill((0,0,0,255)) + # s.blit(screen, (0,0)) + # s.fill((222,0,0,50), (0,0,40,40)) + # pg.image.save_extended(s, name+'.png') + # pg.image.save(s, name+'.png') + # pg.image.save(screen, name+'_screen.png') + # pg.image.save(s, name+'.tga') + pg.image.save(screen, name + ".png") + elif e.type == pg.QUIT: + raise SystemExit() + + +def main(): + """show various surfarray effects + """ + import numpy as N + from numpy import int32, uint8, uint + + pg.init() + print("Using %s" % surfarray.get_arraytype().capitalize()) + print("Press the mouse button to advance image.") + print('Press the "s" key to save the current image.') + + # allblack + allblack = N.zeros((128, 128), int32) + surfdemo_show(allblack, "allblack") + + # striped + # the element type is required for N.zeros in numpy else + # an array of float is returned. + striped = N.zeros((128, 128, 3), int32) + striped[:] = (255, 0, 0) + striped[:, ::3] = (0, 255, 255) + surfdemo_show(striped, "striped") + + # rgbarray + imagename = os.path.join(main_dir, "data", "arraydemo.bmp") + imgsurface = pg.image.load(imagename) + rgbarray = surfarray.array3d(imgsurface) + surfdemo_show(rgbarray, "rgbarray") + + # flipped + flipped = rgbarray[:, ::-1] + surfdemo_show(flipped, "flipped") + + # scaledown + scaledown = rgbarray[::2, ::2] + surfdemo_show(scaledown, "scaledown") + + # scaleup + # the element type is required for N.zeros in numpy else + # an #array of floats is returned. + shape = rgbarray.shape + scaleup = N.zeros((shape[0] * 2, shape[1] * 2, shape[2]), int32) + scaleup[::2, ::2, :] = rgbarray + scaleup[1::2, ::2, :] = rgbarray + scaleup[:, 1::2] = scaleup[:, ::2] + surfdemo_show(scaleup, "scaleup") + + # redimg + redimg = N.array(rgbarray) + redimg[:, :, 1:] = 0 + surfdemo_show(redimg, "redimg") + + # soften + # having factor as an array forces integer upgrade during multiplication + # of rgbarray, even for numpy. + factor = N.array((8,), int32) + soften = N.array(rgbarray, int32) + soften[1:, :] += rgbarray[:-1, :] * factor + soften[:-1, :] += rgbarray[1:, :] * factor + soften[:, 1:] += rgbarray[:, :-1] * factor + soften[:, :-1] += rgbarray[:, 1:] * factor + soften //= 33 + surfdemo_show(soften, "soften") + + # crossfade (50%) + src = N.array(rgbarray) + dest = N.zeros(rgbarray.shape) # dest is float64 by default. + dest[:] = 20, 50, 100 + diff = (dest - src) * 0.50 + xfade = src + diff.astype(uint) + surfdemo_show(xfade, "xfade") + + # alldone + pg.quit() + + +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/audiocapture.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/audiocapture.py new file mode 100644 index 0000000..0157bc3 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/audiocapture.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +""" pygame.examples.audiocapture + +A pygame 2 experiment. + +* record sound from a microphone +* play back the recorded sound +""" +import pygame as pg +import time + +if pg.get_sdl_version()[0] < 2: + raise SystemExit("This example requires pygame 2 and SDL2.") + +from pygame._sdl2 import ( + get_audio_device_name, + get_num_audio_devices, + AudioDevice, + AUDIO_F32, + AUDIO_ALLOW_FORMAT_CHANGE, +) +from pygame._sdl2.mixer import set_post_mix + + +pg.mixer.pre_init(44100, 32, 2, 512) +pg.init() + +# init_subsystem(INIT_AUDIO) +names = [get_audio_device_name(x, 1) for x in range(get_num_audio_devices(1))] +print(names) + +iscapture = 1 +sounds = [] +sound_chunks = [] + + +def callback(audiodevice, audiomemoryview): + """ This is called in the sound thread. + + Note, that the frequency and such you request may not be what you get. + """ + # print(type(audiomemoryview), len(audiomemoryview)) + # print(audiodevice) + sound_chunks.append(bytes(audiomemoryview)) + + +def postmix_callback(postmix, audiomemoryview): + """ This is called in the sound thread. + + At the end of mixing we get this data. + """ + print(type(audiomemoryview), len(audiomemoryview)) + print(postmix) + + +set_post_mix(postmix_callback) + +audio = AudioDevice( + devicename=names[0], + iscapture=1, + frequency=44100, + audioformat=AUDIO_F32, + numchannels=2, + chunksize=512, + allowed_changes=AUDIO_ALLOW_FORMAT_CHANGE, + callback=callback, +) +# start recording. +audio.pause(0) + +print("recording with :%s:" % names[0]) +time.sleep(5) + + +print("Turning data into a pg.mixer.Sound") +sound = pg.mixer.Sound(buffer=b"".join(sound_chunks)) + +print("playing back recorded sound") +sound.play() +time.sleep(5) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/blend_fill.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/blend_fill.py new file mode 100644 index 0000000..301888b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/blend_fill.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +""" pygame.examples.blend_fill + +BLEND_ing colors in different ways with Surface.fill(). + +Keyboard Controls: + +* Press R, G, B to increase the color channel values, +* 1-9 to set the step range for the increment, +* A - ADD, S- SUB, M- MULT, - MIN, + MAX to change the blend modes + +""" +import os +import pygame as pg +from pygame import K_1, K_2, K_3, K_4, K_5, K_6, K_7, K_8, K_9 + + +def usage(): + print("Press R, G, B to increase the color channel values,") + print("1-9 to set the step range for the increment,") + print("A - ADD, S- SUB, M- MULT, - MIN, + MAX") + print(" to change the blend modes") + + +main_dir = os.path.split(os.path.abspath(__file__))[0] +data_dir = os.path.join(main_dir, "data") + + +def main(): + color = [0, 0, 0] + changed = False + blendtype = 0 + step = 5 + + pg.init() + screen = pg.display.set_mode((640, 480), 0, 32) + screen.fill((100, 100, 100)) + + image = pg.image.load(os.path.join(data_dir, "liquid.bmp")).convert() + blendimage = pg.image.load(os.path.join(data_dir, "liquid.bmp")).convert() + screen.blit(image, (10, 10)) + screen.blit(blendimage, (200, 10)) + + pg.display.flip() + pg.key.set_repeat(500, 30) + usage() + + going = True + while going: + for event in pg.event.get(): + if event.type == pg.QUIT: + going = False + + if event.type == pg.KEYDOWN: + usage() + + if event.key == pg.K_ESCAPE: + going = False + + if event.key == pg.K_r: + color[0] += step + if color[0] > 255: + color[0] = 0 + changed = True + + elif event.key == pg.K_g: + color[1] += step + if color[1] > 255: + color[1] = 0 + changed = True + + elif event.key == pg.K_b: + color[2] += step + if color[2] > 255: + color[2] = 0 + changed = True + + elif event.key == pg.K_a: + blendtype = pg.BLEND_ADD + changed = True + elif event.key == pg.K_s: + blendtype = pg.BLEND_SUB + changed = True + elif event.key == pg.K_m: + blendtype = pg.BLEND_MULT + changed = True + elif event.key == pg.K_PLUS: + blendtype = pg.BLEND_MAX + changed = True + elif event.key == pg.K_MINUS: + blendtype = pg.BLEND_MIN + changed = True + + elif event.key in (K_1, K_2, K_3, K_4, K_5, K_6, K_7, K_8, K_9): + step = int(event.unicode) + + if changed: + screen.fill((100, 100, 100)) + screen.blit(image, (10, 10)) + blendimage.blit(image, (0, 0)) + # blendimage.fill (color, (0, 0, 20, 20), blendtype) + blendimage.fill(color, None, blendtype) + screen.blit(blendimage, (200, 10)) + print( + "Color: %s, Pixel (0,0): %s" + % (tuple(color), [blendimage.get_at((0, 0))]) + ) + changed = False + pg.display.flip() + + pg.quit() + + +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/blit_blends.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/blit_blends.py new file mode 100644 index 0000000..bf01e84 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/blit_blends.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python +""" pygame.examples.blit_blends + +Blending colors in different ways with different blend modes. + +It also shows some tricks with the surfarray. +Including how to do additive blending. + + +Keyboard Controls +----------------- + +* R, G, B - add a bit of Red, Green, or Blue. +* A - Add blend mode +* S - Subtractive blend mode +* M - Multiply blend mode +* = key BLEND_MAX blend mode. +* - key BLEND_MIN blend mode. +* 1, 2, 3, 4 - use different images. + +""" +import os +import pygame as pg +import time + +main_dir = os.path.split(os.path.abspath(__file__))[0] +data_dir = os.path.join(main_dir, "data") + +try: + import pygame.surfarray + import numpy +except ImportError: + print("no surfarray for you! install numpy") + + +def main(): + pg.init() + pg.mixer.quit() # remove ALSA underflow messages for Debian squeeze + screen = pg.display.set_mode((640, 480)) + + im1 = pg.Surface(screen.get_size()) + # im1= im1.convert() + im1.fill((100, 0, 0)) + + im2 = pg.Surface(screen.get_size()) + im2.fill((0, 50, 0)) + # we make a srcalpha copy of it. + # im3= im2.convert(SRCALPHA) + im3 = im2 + im3.set_alpha(127) + + images = {} + images[pg.K_1] = im2 + images[pg.K_2] = pg.image.load(os.path.join(data_dir, "chimp.bmp")) + images[pg.K_3] = pg.image.load(os.path.join(data_dir, "alien3.gif")) + images[pg.K_4] = pg.image.load(os.path.join(data_dir, "liquid.bmp")) + img_to_blit = im2.convert() + iaa = img_to_blit.convert_alpha() + + blits = {} + blits[pg.K_a] = pg.BLEND_ADD + blits[pg.K_s] = pg.BLEND_SUB + blits[pg.K_m] = pg.BLEND_MULT + blits[pg.K_EQUALS] = pg.BLEND_MAX + blits[pg.K_MINUS] = pg.BLEND_MIN + + blitsn = {} + blitsn[pg.K_a] = "BLEND_ADD" + blitsn[pg.K_s] = "BLEND_SUB" + blitsn[pg.K_m] = "BLEND_MULT" + blitsn[pg.K_EQUALS] = "BLEND_MAX" + blitsn[pg.K_MINUS] = "BLEND_MIN" + + screen.blit(im1, (0, 0)) + pg.display.flip() + clock = pg.time.Clock() + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + + going = True + while going: + clock.tick(60) + + for event in pg.event.get(): + if event.type == pg.QUIT: + going = False + if event.type == pg.KEYDOWN: + usage() + + if event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE: + going = False + + elif event.type == pg.KEYDOWN and event.key in images.keys(): + img_to_blit = images[event.key] + iaa = img_to_blit.convert_alpha() + + elif event.type == pg.KEYDOWN and event.key in blits.keys(): + t1 = time.time() + # blits is a dict keyed with key -> blit flag. eg BLEND_ADD. + im1.blit(img_to_blit, (0, 0), None, blits[event.key]) + t2 = time.time() + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + print("time to do:%s:" % (t2 - t1)) + + elif event.type == pg.KEYDOWN and event.key in [pg.K_t]: + + for bkey in blits.keys(): + t1 = time.time() + + for x in range(300): + im1.blit(img_to_blit, (0, 0), None, blits[bkey]) + + t2 = time.time() + + # show which key we're doing... + onedoing = blitsn[bkey] + print("time to do :%s: is :%s:" % (onedoing, t2 - t1)) + + elif event.type == pg.KEYDOWN and event.key in [pg.K_o]: + t1 = time.time() + # blits is a dict keyed with key -> blit flag. eg BLEND_ADD. + im1.blit(iaa, (0, 0)) + t2 = time.time() + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + print("time to do:%s:" % (t2 - t1)) + + elif event.type == pg.KEYDOWN and event.key == pg.K_SPACE: + # this additive blend without clamp two surfaces. + # im1.set_alpha(127) + # im1.blit(im1, (0,0)) + # im1.set_alpha(255) + t1 = time.time() + + im1p = pygame.surfarray.pixels2d(im1) + im2p = pygame.surfarray.pixels2d(im2) + im1p += im2p + del im1p + del im2p + t2 = time.time() + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + print("time to do:%s:" % (t2 - t1)) + + elif event.type == pg.KEYDOWN and event.key in [pg.K_z]: + t1 = time.time() + im1p = pygame.surfarray.pixels3d(im1) + im2p = pygame.surfarray.pixels3d(im2) + im1p16 = im1p.astype(numpy.uint16) + im2p16 = im1p.astype(numpy.uint16) + im1p16 += im2p16 + im1p16 = numpy.minimum(im1p16, 255) + pygame.surfarray.blit_array(im1, im1p16) + + del im1p + del im2p + t2 = time.time() + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + print("time to do:%s:" % (t2 - t1)) + + elif event.type == pg.KEYDOWN and event.key in [pg.K_r, pg.K_g, pg.K_b]: + # this adds one to each pixel. + colmap = {} + colmap[pg.K_r] = 0x10000 + colmap[pg.K_g] = 0x00100 + colmap[pg.K_b] = 0x00001 + im1p = pygame.surfarray.pixels2d(im1) + im1p += colmap[event.key] + del im1p + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + + elif event.type == pg.KEYDOWN and event.key == pg.K_p: + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + + elif event.type == pg.KEYDOWN and event.key == pg.K_f: + # this additive blend without clamp two surfaces. + + t1 = time.time() + im1.set_alpha(127) + im1.blit(im2, (0, 0)) + im1.set_alpha(255) + + t2 = time.time() + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + print("time to do:%s:" % (t2 - t1)) + + screen.blit(im1, (0, 0)) + pg.display.flip() + + pg.quit() + + +def usage(): + print("press keys 1-5 to change image to blit.") + print("A - ADD, S- SUB, M- MULT, - MIN, + MAX") + print("T - timing test for special blend modes.") + + +if __name__ == "__main__": + usage() + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/camera.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/camera.py new file mode 100644 index 0000000..7eca9c8 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/camera.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +""" pygame.examples.camera + +Basic image capturing and display using pygame.camera + +Keyboard controls +----------------- + +- 0, start camera 0. +- 1, start camera 1. +- 9, start camera 9. +- 10, start camera... wait a minute! There's not 10 key! +""" +import pygame as pg +import pygame.camera + + +class VideoCapturePlayer(object): + + size = (640, 480) + + def __init__(self, **argd): + self.__dict__.update(**argd) + super(VideoCapturePlayer, self).__init__(**argd) + + # create a display surface. standard pygame stuff + self.display = pg.display.set_mode(self.size, 0) + self.init_cams(0) + + def init_cams(self, which_cam_idx): + + # gets a list of available cameras. + self.clist = pygame.camera.list_cameras() + print(self.clist) + + if not self.clist: + raise ValueError("Sorry, no cameras detected.") + + try: + cam_id = self.clist[which_cam_idx] + except IndexError: + cam_id = self.clist[0] + + # creates the camera of the specified size and in RGB colorspace + self.camera = pygame.camera.Camera(cam_id, self.size, "RGB") + + # starts the camera + self.camera.start() + + self.clock = pg.time.Clock() + + # create a surface to capture to. for performance purposes, you want the + # bit depth to be the same as that of the display surface. + self.snapshot = pg.surface.Surface(self.size, 0, self.display) + + def get_and_flip(self): + # if you don't want to tie the framerate to the camera, you can check and + # see if the camera has an image ready. note that while this works + # on most cameras, some will never return true. + + self.snapshot = self.camera.get_image(self.display) + + # if 0 and self.camera.query_image(): + # # capture an image + + # self.snapshot = self.camera.get_image(self.snapshot) + + # if 0: + # self.snapshot = self.camera.get_image(self.snapshot) + # # self.snapshot = self.camera.get_image() + + # # blit it to the display surface. simple! + # self.display.blit(self.snapshot, (0, 0)) + # else: + + # self.snapshot = self.camera.get_image(self.display) + # # self.display.blit(self.snapshot, (0,0)) + + pg.display.flip() + + def main(self): + going = True + while going: + events = pg.event.get() + for e in events: + if e.type == pg.QUIT or (e.type == pg.KEYDOWN and e.key == pg.K_ESCAPE): + going = False + if e.type == pg.KEYDOWN: + if e.key in range(pg.K_0, pg.K_0 + 10): + self.init_cams(e.key - pg.K_0) + + self.get_and_flip() + self.clock.tick() + print(self.clock.get_fps()) + + +def main(): + pg.init() + pygame.camera.init() + VideoCapturePlayer().main() + pg.quit() + + +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/chimp.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/chimp.py new file mode 100644 index 0000000..1375d57 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/chimp.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python +""" pygame.examples.chimp + +This simple example is used for the line-by-line tutorial +that comes with pygame. It is based on a 'popular' web banner. +Note there are comments here, but for the full explanation, +follow along in the tutorial. +""" + + +# Import Modules +import os +import pygame as pg +from pygame.compat import geterror + +if not pg.font: + print("Warning, fonts disabled") +if not pg.mixer: + print("Warning, sound disabled") + +main_dir = os.path.split(os.path.abspath(__file__))[0] +data_dir = os.path.join(main_dir, "data") + + +# functions to create our resources +def load_image(name, colorkey=None): + fullname = os.path.join(data_dir, name) + try: + image = pg.image.load(fullname) + except pg.error: + print("Cannot load image:", fullname) + raise SystemExit(str(geterror())) + image = image.convert() + if colorkey is not None: + if colorkey == -1: + colorkey = image.get_at((0, 0)) + image.set_colorkey(colorkey, pg.RLEACCEL) + return image, image.get_rect() + + +def load_sound(name): + class NoneSound: + def play(self): + pass + + if not pg.mixer or not pg.mixer.get_init(): + return NoneSound() + fullname = os.path.join(data_dir, name) + try: + sound = pg.mixer.Sound(fullname) + except pg.error: + print("Cannot load sound: %s" % fullname) + raise SystemExit(str(geterror())) + return sound + + +# classes for our game objects +class Fist(pg.sprite.Sprite): + """moves a clenched fist on the screen, following the mouse""" + + def __init__(self): + pg.sprite.Sprite.__init__(self) # call Sprite initializer + self.image, self.rect = load_image("fist.bmp", -1) + self.punching = 0 + + def update(self): + """move the fist based on the mouse position""" + pos = pg.mouse.get_pos() + self.rect.midtop = pos + if self.punching: + self.rect.move_ip(5, 10) + + def punch(self, target): + """returns true if the fist collides with the target""" + if not self.punching: + self.punching = 1 + hitbox = self.rect.inflate(-5, -5) + return hitbox.colliderect(target.rect) + + def unpunch(self): + """called to pull the fist back""" + self.punching = 0 + + +class Chimp(pg.sprite.Sprite): + """moves a monkey critter across the screen. it can spin the + monkey when it is punched.""" + + def __init__(self): + pg.sprite.Sprite.__init__(self) # call Sprite intializer + self.image, self.rect = load_image("chimp.bmp", -1) + screen = pg.display.get_surface() + self.area = screen.get_rect() + self.rect.topleft = 10, 10 + self.move = 9 + self.dizzy = 0 + + def update(self): + """walk or spin, depending on the monkeys state""" + if self.dizzy: + self._spin() + else: + self._walk() + + def _walk(self): + """move the monkey across the screen, and turn at the ends""" + newpos = self.rect.move((self.move, 0)) + if not self.area.contains(newpos): + if self.rect.left < self.area.left or self.rect.right > self.area.right: + self.move = -self.move + newpos = self.rect.move((self.move, 0)) + self.image = pg.transform.flip(self.image, 1, 0) + self.rect = newpos + + def _spin(self): + """spin the monkey image""" + center = self.rect.center + self.dizzy = self.dizzy + 12 + if self.dizzy >= 360: + self.dizzy = 0 + self.image = self.original + else: + rotate = pg.transform.rotate + self.image = rotate(self.original, self.dizzy) + self.rect = self.image.get_rect(center=center) + + def punched(self): + """this will cause the monkey to start spinning""" + if not self.dizzy: + self.dizzy = 1 + self.original = self.image + + +def main(): + """this function is called when the program starts. + it initializes everything it needs, then runs in + a loop until the function returns.""" + # Initialize Everything + pg.init() + screen = pg.display.set_mode((468, 60)) + pg.display.set_caption("Monkey Fever") + pg.mouse.set_visible(0) + + # Create The Backgound + background = pg.Surface(screen.get_size()) + background = background.convert() + background.fill((250, 250, 250)) + + # Put Text On The Background, Centered + if pg.font: + font = pg.font.Font(None, 36) + text = font.render("Pummel The Chimp, And Win $$$", 1, (10, 10, 10)) + textpos = text.get_rect(centerx=background.get_width() / 2) + background.blit(text, textpos) + + # Display The Background + screen.blit(background, (0, 0)) + pg.display.flip() + + # Prepare Game Objects + clock = pg.time.Clock() + whiff_sound = load_sound("whiff.wav") + punch_sound = load_sound("punch.wav") + chimp = Chimp() + fist = Fist() + allsprites = pg.sprite.RenderPlain((fist, chimp)) + + # Main Loop + going = True + while going: + clock.tick(60) + + # Handle Input Events + for event in pg.event.get(): + if event.type == pg.QUIT: + going = False + elif event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE: + going = False + elif event.type == pg.MOUSEBUTTONDOWN: + if fist.punch(chimp): + punch_sound.play() # punch + chimp.punched() + else: + whiff_sound.play() # miss + elif event.type == pg.MOUSEBUTTONUP: + fist.unpunch() + + allsprites.update() + + # Draw Everything + screen.blit(background, (0, 0)) + allsprites.draw(screen) + pg.display.flip() + + pg.quit() + + +# Game Over + + +# this calls the 'main' function when this script is executed +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/cursors.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/cursors.py new file mode 100644 index 0000000..c734cb8 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/cursors.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +""" pygame.examples.cursors + +Click a mouse button (if you have one!) and the cursor changes. + +""" +import pygame as pg + + +arrow = ( + "xX ", + "X.X ", + "X..X ", + "X...X ", + "X....X ", + "X.....X ", + "X......X ", + "X.......X ", + "X........X ", + "X.........X ", + "X......XXXXX ", + "X...X..X ", + "X..XX..X ", + "X.X XX..X ", + "XX X..X ", + "X X..X ", + " X..X ", + " X..X ", + " X..X ", + " XX ", + " ", + " ", + " ", + " ", +) + + +no = ( + " ", + " ", + " XXXXXX ", + " XX......XX ", + " X..........X ", + " X....XXXX....X ", + " X...XX XX...X ", + " X.....X X...X ", + " X..X...X X..X ", + " X...XX...X X...X ", + " X..X X...X X..X ", + " X..X X...X X..X ", + " X..X X.,.X X..X ", + " X..X X...X X..X ", + " X...X X...XX...X ", + " X..X X...X..X ", + " X...X X.....X ", + " X...XX X...X ", + " X....XXXXX...X ", + " X..........X ", + " XX......XX ", + " XXXXXX ", + " ", + " ", +) + + +def TestCursor(arrow): + hotspot = None + for y in range(len(arrow)): + for x in range(len(arrow[y])): + if arrow[y][x] in ["x", ",", "O"]: + hotspot = x, y + break + if hotspot is not None: + break + if hotspot is None: + raise Exception("No hotspot specified for cursor '%s'!" % cursorname) + s2 = [] + for line in arrow: + s2.append(line.replace("x", "X").replace(",", ".").replace("O", "o")) + cursor, mask = pg.cursors.compile(s2, "X", ".", "o") + size = len(arrow[0]), len(arrow) + pg.mouse.set_cursor(size, hotspot, cursor, mask) + + +def main(): + pg.init() + pg.font.init() + font = pg.font.Font(None, 24) + bg = pg.display.set_mode((800, 600), 0, 24) + bg.fill((255, 255, 255)) + bg.blit(font.render("Click to advance", 1, (0, 0, 0)), (0, 0)) + pg.display.update() + for cursor in [no, arrow]: + TestCursor(cursor) + going = True + while going: + pg.event.pump() + for e in pg.event.get(): + if e.type == pg.MOUSEBUTTONDOWN: + going = False + pg.quit() + + +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien1.gif b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien1.gif new file mode 100644 index 0000000..c4497e0 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien1.gif differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien1.jpg b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien1.jpg new file mode 100644 index 0000000..6d110a4 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien1.jpg differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien1.png b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien1.png new file mode 100644 index 0000000..471d6a4 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien1.png differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien2.gif b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien2.gif new file mode 100644 index 0000000..8df05a3 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien2.gif differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien2.png b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien2.png new file mode 100644 index 0000000..aef5ace Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien2.png differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien3.gif b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien3.gif new file mode 100644 index 0000000..5305d41 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien3.gif differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien3.png b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien3.png new file mode 100644 index 0000000..90d0f7c Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/alien3.png differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/arraydemo.bmp b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/arraydemo.bmp new file mode 100644 index 0000000..ad96338 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/arraydemo.bmp differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/asprite.bmp b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/asprite.bmp new file mode 100644 index 0000000..cc96356 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/asprite.bmp differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/background.gif b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/background.gif new file mode 100644 index 0000000..5041ce6 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/background.gif differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/blue.mpg b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/blue.mpg new file mode 100644 index 0000000..60dceca Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/blue.mpg differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/bomb.gif b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/bomb.gif new file mode 100644 index 0000000..02271c3 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/bomb.gif differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/boom.wav b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/boom.wav new file mode 100644 index 0000000..f19126a Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/boom.wav differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/brick.png b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/brick.png new file mode 100644 index 0000000..cfe37a3 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/brick.png differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/car_door.wav b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/car_door.wav new file mode 100644 index 0000000..60acf9e Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/car_door.wav differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/chimp.bmp b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/chimp.bmp new file mode 100644 index 0000000..ec5f88a Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/chimp.bmp differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/city.png b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/city.png new file mode 100644 index 0000000..202da5c Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/city.png differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/danger.gif b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/danger.gif new file mode 100644 index 0000000..106d69c Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/danger.gif differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/explosion1.gif b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/explosion1.gif new file mode 100644 index 0000000..fabec16 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/explosion1.gif differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/fist.bmp b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/fist.bmp new file mode 100644 index 0000000..a75f12e Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/fist.bmp differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/house_lo.mp3 b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/house_lo.mp3 new file mode 100644 index 0000000..4c26994 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/house_lo.mp3 differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/house_lo.ogg b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/house_lo.ogg new file mode 100644 index 0000000..e050848 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/house_lo.ogg differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/house_lo.wav b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/house_lo.wav new file mode 100644 index 0000000..68a96b8 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/house_lo.wav differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/liquid.bmp b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/liquid.bmp new file mode 100644 index 0000000..c4f12eb Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/liquid.bmp differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/midikeys.png b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/midikeys.png new file mode 100644 index 0000000..74ecb86 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/midikeys.png differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/oldplayer.gif b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/oldplayer.gif new file mode 100644 index 0000000..93906ab Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/oldplayer.gif differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/player1.gif b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/player1.gif new file mode 100644 index 0000000..6c4eda7 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/player1.gif differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/punch.wav b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/punch.wav new file mode 100644 index 0000000..aa3f56c Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/punch.wav differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/sans.ttf b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/sans.ttf new file mode 100644 index 0000000..09fac2f Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/sans.ttf differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/secosmic_lo.wav b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/secosmic_lo.wav new file mode 100644 index 0000000..867f802 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/secosmic_lo.wav differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/shot.gif b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/shot.gif new file mode 100644 index 0000000..18de528 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/shot.gif differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/static.png b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/static.png new file mode 100644 index 0000000..fb3b057 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/static.png differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/whiff.wav b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/whiff.wav new file mode 100644 index 0000000..3954efa Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/whiff.wav differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/yuv_1.pgm b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/yuv_1.pgm new file mode 100644 index 0000000..a59b383 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/data/yuv_1.pgm @@ -0,0 +1,6 @@ +P5 +752 864 +255 +                                        +                                                                            >Zideigcgffedcbbeeeeeeeedddddddd_adglortsuy~xwyy{|w}~ytvtvrxuw{vwvvwwusrtutnnoligdgkkhhlnnoopppoononkjjd_crxqnrqqlihmnkkmklmnnnmlkghiiigfegggghhhhkgeedbdgeeeeeeeecegfeddeefilpsuwuw||xvvxwwwz{{y~~|{yxwv{{{{{{{{vvuvvvvvxvuuwwvsstwyyuvwsoorsoquuttrponmusrmhlojflppnlifgmpqrlhioqrokhikstsniegisqmigiospqkcckqqnkd`hrnbQJC3,.,-OU>0444.&0>FO[T<3FRPMTZ[MKILQLDDMLIHHGDCFXZEBY_IAYeO8@Wa]][ZZZ[\^XRK<,*00259=98666=<>=97EW<%$#.@DSgkVKsdC{[_eQ<555512369;=?BEABDFHIIIHIJKLLLLPPQQRSSTTTUUVWWXZ[\]_`bbcccddeeegggffeeegghhiijjmmkjiiiikkllnnopnooppqqrpqrqpnljja`aWUcrvnmpxqN25=<<==?79521/--0**+++,,,)07;:89;CCDDGIKLNNNNNMMMRRRRRRRR=ctkhmjekkjigfddddddddddcccbbbbaeeddddcceeddddccbcdgilmnnoqvz{ǰjrxtrrtvvnu}}}wx{wz{~{yxurtzspmmonifflnjglpqqrrsrrqponihjd`gnypntsrjgfkmhjononmlklmnkklmlkihjjkkklllokggfegkiiiiiiiigffgiigeggffeeddfecdefedhjloruwx~}}}}||||{{zzyxxx|{zz||{z|yvvxxurtwyzxwxzuppuursxuututttttstoinqidiorojfdgloqqjehorsohefimoolfdgksqmhehotqsmaakqomlc]fso`RKD2+1-0NY<0940(&1?GOaX60HUNIR[[ZVTVXSNPKIQQDDLJ?JPHFW\NL\d^M?DV]_\XZ\ZYbWNH9))41258;=>>?><:89;><9;?:5?QA''% /=CXacWJofIv`[hT;534;89;>ABCDGJGHIJJIIIJKLNOPPPRRSTVWXXZZ[\]__`abbccddeccddeeffhhhhggggeffhijkkkkjjklmnoooooppplkkjiihh`_\ZVRONH??A;A]vrptswrM//5//.,0*.,*+0588579;>ACDDIOQONOQTSRRQRRRVVWXYZ[[`abcdefgBgvlkpnmqqponmlliiiiiiiiiihhggffhggggfffffghiijjiiiiiiiieffedcddcd_ahecknprtwz|}ϼ[emhioomgdyrxvywxqr||vuvwyslopgilkgipspuuuuutssrogeg`\expntqsnjeilhmtrponmlmnnmmoonljimmmmnnnnplkkkkmplllllllljihjkkihllllllllonmllljigghijklljhebbcfhhimqw|~}|~|z{|}{y}yyzzuqu|urrtutuxuuvvwwxxpptrmrtkfhnsogcfjmnnmgflnpqmhdegmookecgkppokhinrqsnfekolmnhafqpfQJH5/601PV;/61-(&4CHR`T12HTMGQ[^TILSOPWXY[b^QMTWKOPE?LSHGZ_SJGHLN[^XY[[Z_SJE9**302479::9<==<;;=>=77;95?OC*'##187BAAILMLIHJLMMMMMLKJPQSUVWWWWWXYZZ[[^^_``abbeeddcbbbcccdeeffffghiijjiijjklmmllkkllmnkjihfedcZYVSOLIHHGEBAABCB@LMJPPQVTOY`XSdeNBPQ>>Wc]Y[^bXSPI9((12358998748<>=:9999<=73@SD#() 2@CVgdSLqfG}b_fY??=9ADNOQSQMKLOKMNQRTTTSTVXYZ[[\]]]]^^^````aaaabbbbbcccccddefggdefhjlmnjjjkkkllgec`][YYUTROLJHFDDCBA@@?=>?AABAAFEIH@Ldojp{svvL)'.6R[QVVTgwvqqrqtttsssrrtojilnljmmlmmopqssrqponnlmnopqqrGnxptuqsxusttsrroqsuurrwtrssqsurtqqsuwuqrqqsrppsqpsusmjkppqrolmphmnkhhjjmigihghliffhgdbcdeeeddegc_cgccgecbcfjors|pgghgjo\PKNPNJC<<=>?CFENQQLLJ5.B^`[\\`^TPI8)*0/14789:;<;9:<:9<9=<=;8DU@'&%!.:FTefSLqcEza]cVHB=:@GOTTTUTSSTRQSWZZYYWUZ_]\^^]]^_^^`cba`abcefdccdedegeeghhmojjihijhebfb][ZXSOOMJHHHGFBBAAA??DC@ACCDEDJKKMOPRSXX[ZTVesopurwuQ01:I_hghc]ntqstsvvwwwy{{z|xpkjjkmrnlnppprppqponpsqrqpopqrGmyrvxutqtvtssttvxwtttrrutrrtwusstvvtttuurrtussusttrqpoorqstspquusqqpnnpsolllknrljjnqnjhnljiiihggjmjdgigfedcbcdedddddcddcdec^``[YVUVUSOKOKMNMX^UG@IVXEAb}ZOplKdzvzvszpfiw~}yttsswxusqxwvwxxuqwtqqtspmqtqruttvrssrtvvtprsrqrrrquvwxut{vuuvsqtyvqsvtrsuvvvwvuuuwtqqsusqpqqpqsrnqoooommmjiikkjknhgeddfgigghmnnrw|~w{~y{zz|yx||vpmkknleipsqoidgoqpomfdjrrplfdioprphdhmnpoibdlonlke_enj^UNK8-411IWA141.)'7DGVaM67MRNIM[W64MJ?ORLJV?1HJ79SD;745996;78;?@816IW]]XZ`TLE3(+213566777:=<8789;;:8=<4@UA%%&#,9GWhjXPtdEzb]cVJIHHNRW\ZY[[ZYYYYXYZ]^^[\[\]^^^`a``^^^__`a`_beggfgiihghggif``b^`\VRPNMKLJHHHHFD??@CEDCDCCCEGFGJNMORSTVWXZ]`aa_^c`_\VXgvvppqywS39JXfklnjhqvy{{{~wljomknolmqspnoroorqoostrqrqprvDn~wwvtwxwvxyyxvyxvwxutwxytrvvsrsuvtropruqortrqquvtpqvxwuttutqqtvtstvvtrpoqttrqqpnnrtspnrpnlllkinmmnnljgklkjhgijkfeggefhdefc``a_ec``c_[]^[\[UQPLQOQNONIOVPL]WHhzyyzup~x\Yv_Yq|}zjpuvtttsxwponqotvwwxwsurrrrsttssssssqsusvvxzvsvywwxwtuwxtvxvz{uvvwxwuwztvxwwwuqswxuuvrlwurprtsptpnqtrnmsqpooprsolklnmmnljjklijmqmoqt{~uy{~u“rrxyylonlmheoropiekopoonhflqopmeelpqtpgdflrsphaclrrmnh`frn_WMK9/312IWA141-)(6DJY_I4:NPNNS]V53KB:RTMUR46[Q7FWKLQNKLHDFHHGKPJ906IZ^Z\^QJC3(+0025677889:98:<96=;9<<6@SC$$'$+7HXggSLrdEza]cXOQSW\\\a^\\]]]]Z\]ZZ[\[\^[Y]^\\aa`^_bca`figdddc`__`^ZWVTQQOMLJDFEDDEEDCCCCBBAAAFGIJKKLLNOOSWWWZVY__YX`hcdfgiijie\WUUZemrlnpwrT?AB@@@@BBCBBEGHJJKLNQUWVUYZX]]]`dcabhgihb_beggeddefgbUKMQ[ekqmpqtoS?J]]dnmnngceijjhdfghiijlnlmoomnompoooonmlpnlmqspknqrrstsqF{w~i^gnkquwxz||}|z{zz|~{w{{{}xxwrpsvurqurqruuuuyvvxxvvwwwvtuvwvsttttuuuuvvtrrstsstsssuwnstpnorutsrrsssrlpqpnnoplmliiknogejh`bif_`^\^\Z\[\ZY`^Y]XY`[\^[ckpppnmrk\UUUQU\YWY_[X`_UTWYZZYTNNQ[ua_bagjjlkknqrrrstvwxyz{{~~yywz|yz|~~|{{}{~{wvx{~}{yz|ys{{{yyxxxw{{xx{}|}xvvvuvxvwz{{xvtuvvtssuutomoqqpqqnossqokroquxyzyodadaacbafijmllqst|tm|zoknmiijmopnnjhhoomnledlprqkfchqpqqkehqsqrlcagnpqme`gpnfWMK;1302NW>/64.%(;GJ[`E.9LRNHT`M7HVPQ>)F\B>ZdMEYWIFU^OEKGNbV;EO6,32/5?LW\NIB1)./13678899789;=:9;=74<=5?SA''%",9EUegUPv_F\\h`WWWZa_\^`a_`a_Y\]^__^_a^]__][\[XVVVVRNKKKIECEFECAAA@>>?=@BA@?BFGHJLMNPPTVYZZ[^_]`ddcba`cbacfdabgdegecb`cddeffggcWNLP[myuoqpxuV/64-$)>HIZ_C.ATWRMW_K/;>8<2*EMFBFLOKEFNLHJIFKDN[N>KK4162112CXZMIC0*///136789:9<;89:::;85;<7ASA)'%!-9DVggTMt_F[\h`XXUXacaaca^_bb^[[]_^[XYVRRRKJLMJGEFEDDFA@?>>>>>=<=@CDFIIKLNPOPTXY[\\]^`bccb```a`^_cb^]`]]]_cbadfcceefhhghhhhgffcWONR^nytpppxxX<[{mcnmljhkonknnilmlkmpoljpnknmjonoonnpqpopqqppppppprrsrr<^ihrsmnsvshacjmlijpvutw}~¿~~|z{|ysstspqw{{vxxvuvuswvttuuutuqtyxuusssstttttwrosvtpotrpqtoknlonnpmhfedcbacdb`agh`]ccgeabfa]blponprql_[^bb_`acaddaacfbaced_^`ba^^_^\Za]^`^^b__bb_]]]\\^]ZY[\\[\\`hjlqtsuy~y}}|||~y|~}}}}z|~|yy}{zzxvw{yyzzzyxxzzyvwzzv|xuuvuvxvyzwsrstns{|tsy{pollnlikplospmoqurkhjnk_ajqqpkffkprolgdfnpppnfgsqqokfdhnspolggmrqoicempomkfbirncTMK8.410LYA141,'->GJ[\@/EXTRX\]QC?=<8655:9416>=5=FA7;CDBAHLBAH@1.7/,6DU]XLJC/)//.036789:6;=;9878;<9;;9CRC(&&"+8DVeeQJpaF}^]f^YYTV`ccgfd_XTVXTRPNMLKJDCBA@ACDBBBA@?@@EBADDDGLMLNQSSUW\VOQZ^]`___^^_cf]^^^]]]^_\\_a`bdabcehhimogcefggehhgghijkaUMOV_krsruqusW=Y{i_rrnjjjjhikkhgklkjkmnmrnilonoqppqpoooprqopsspmopprtsq<[d\]bcb````dfc^abcdhjjllrttwy|}zvvxyttutsstvwuxxtx|yprttttuvwxwuuvursxvqrsqqlmlmnjilmigeabee]__bcacffijfhf^boxqoqqsl][ckfacfgjbdiedfhcaeea`bb^`b`^^`^c^^`aeb^``^_bb_acdbabba]_ba`bddcdfhmqrqqkmsrtxyxy{~~|wx~{z{~|{z{}~{zww{ztux||xuuvxvuwwsonrxxty~zqooqstronsqnrusqopoldjsfajqpmihjlsqmmhdimoookbdppopmdbjptvqhejnptupfcinpmme]dqpcXPI1'244IYE22-+)0>EJ\Z>1ATJK]]WT\ZSRPLOMOKHECBBB?:9;845;87726=3/153CX[[[YLH@+'0224677888:868;:77<;8<;6@RD&%(#*6FQdgVPucEza]c^ZZTS]a`cee^K;9@EECAABA>BDA?BC@@ADEEEIKLKKOUUQSZVVX\]\[\`\WZba]^[^`a`^__`abccdefcfeceiieefeddbcggfhjfddeihhgghij_VQSV_ltsrurvvZCcmapljmoooonlkkonmnmkmqpnpqpqqmprpllrutoqsqnnqttrpqqppq:]hcgdcjeadeadf^_db_cc_`cffghecffhnx͸~zyywz|vuywuvutvyusuvvywvrvutrmqurnnppooieeefgbbcbbfgfgjkecijg^epsoppqthabcegecddjnhchkhfgededaaa`bb]_cca``begfd_`aaabceedbcfdbedda`cc``a^`eeabhghgfgggklmkknllqhja]ڻ|wx}{w}~~}{z{}~|yw|||}zsnuzyvvxzpsusprtrutuwvssukrytjlogckoopmijiptqojdgpproifillophabjnqppmfhnnrsmcckpqole`emleVQM;*.20IWD166,$,FHI^X;0JWOHPVWUSWYUPQSUUSRSUUUUUVOLRPIIGBGFAA8-16JYa`Y[YOK=+(-/-25589:;988::9:;;;=>;5>RI)&+$)9BVbhVPzdFza^c`XZZZ]^afeg_E25=?@BDDCB@CEGGGHKNNOQRSTTUWVXZ[XWX^]\Z\_`__^^`_]^baabcddb`fffeeeefdgiihghjffgggfedfiheeiiffghhggggcUPTV^ksxttnszbDet`rrlmkmnmlmmmopqpnnpqqqonooonlqsoorrotqprrqqttrsuurpp=`jfigcfdadd`dheghdbdcbcdfedecabdcbcefedkiimnnpsyz}vz||}zwyzwwvxxvstzusrrusoroknolpnbkecjied]afiigcclgjlmjjcenqppsvsk`behgeeihgjllkihbdedcb``_ac`_egabbcdddddbdda`dghhgghhfghffccffccic`dfecddcegedffefdcdefg`ca_x}{{}{wvy}~|x{|{{|yup{~|}wzsrutuwuxxwvvvussvwpjqulfmqtvphgjnqpqnghpoqrlegpnonhbcjmrpkedhloqpjechnspmfbgongUQP=*,22IWD166,%4EHQeX:5K_]UX[YSFOVTONOPQPQUVUVYYWV\^XQOVVWVZWB19V_Z\[Y]YOK=,*.013349;87:<<::<<:<::<86BUD%"(")9CUbiWOxdH}_^g`XYZZ_adhcgeN98?GHIJLMNOTTTRPRUXXWUUWXYYXWVVY\^^\^_]\^``acdcabcecdeeddgigeeigcdjgeccdfghefghgfeehhhigdfidefgghijcXQQS_mstrtrxx]Fb|mdomlmnmmppnnqlkoroopmoonooqqqtpnqsqpprootvtqpssssrssr?hpejlhgjhhgddfghdadhifddfecdedccbbceffeheeffdegfefgfcdfdeffghkmswsnv˔z~uvz{xvwsttvwsmielhecbfd`chjgehigkkhdd`dsxrptrokgbdmoheijkoljlikfefgfeff`efa_dgbdfgecaabafhfdffdhgiifjnmijklomihlihikjjjljklkkookkjhfhhfonifedgf]sÿx}}}wyyupsxusx~vy{ywv{z{zqiosmcjqqmljdinsqpmhlpssplfgopnongcgmrroiefkorojdejpsqngbgong[QJ;/33-IWE166-'2CGPcW90I`bYY[\WRX\YVUVUSRUXWSTX[TV\ZSSWRUPLYZD9Pc\X`]\]YNJ<,*/004558989789;<<;;878:73=NH)%)$)8BVcjVNvfH}__i`WYZ[`cfgdgaQFGITUVVXXYYWXXWVUWY\YWWY[[Z[]\YZ`dd[_dddddbaeggfgfceffffeddfffffeefeffeddgiegihgefffgihggikffhggghheXQOR_ntuuuqyx^N^fahpjoqlnonnnnnmlnnnoonooppqponpoqsrmmpppqtsqqsttrpqtusAmvlqpjorpoppnmosmjlnonmmmkijjiglkihgfddhfegfefheffgeeegjkkkhgedfigfgfdgjjjiijlnmryzy}o}¡}[p{r}piozrcbbfiljhjjhmlefih_ipouqsnebbgijkkolpmlqnomiggfffdffgedfiifhiifffgjfgmldchkhkonkkmmmlmnonmmnnnnqqqmponoooqrpppnookopknnlrsllkjmpmgmmmnprtvwy}ÿu{}zuqttw|{wy||{||}{y{z|xqtvpijmoonkiiosqojflruqnlgeloopmfdksnoqmfgoqoqlcblsroleafnmf[QM>/12/JXF256.).>FRbV9-L^`[[\_\\][YYZVQVWX[YVTUWVY^YPPWZ\TLTWMN^^X[\WY^WMH;*+/0.36689:<779<<978;;:<;4;OI,&*%*7BWciVMvhGya_g_WZ]]bceffe^QPTUZZZZYYXXWYZ[ZZ[\_^^_bcb`befecdfgcdcbdgfdeecbcffeffggfeb`bbbbfihedfhheefidgiigffgfeeffghhefggghijaYSQSanqswvoyz`Q^YXkrmprmpqolmllptpnpnjmomlmnopqlnqrqoprpopstsrrrqruwvrpDnynrutxsrqrusrttqqqonpsvsrrrqpooopoonnnpmmnnmnpmnnkigggjhgefghhffffhddkgeccdefge`cigcdfgjhiokgnofbgkkllnirqoiStz~iol`r^etsmpnftroxqplb`ejjhlpooplntpmnhfghgfejhfgjkjiggghihhglihkkihiloooqqnmonnnopppnopoqsqmtwvqrsstpnpttttqpususptrrpnnpqpnmmkjjklljhgfghihhhhhhkorkpw~selokxw||}|y|z{yrimlntqiijmppplehsqooodbkprrkcdkppmmicgmnkqoc^hsrpmfafnmf[QM?001.JYG256/+*+-9E:+,CMQVZZ\\\^_`dd`YZ[\Z[ZWSTY[[\SNRSUVRUWW_]Y^\VX\`VLG8)*/.02249;988:;99:978:8;=4:QC'"&$)7DVdkWLsfFx_^e^X]_`bbbgdfcWQW[\[ZYZZ\]^^^^_`bd__``aaaababdca`aedcabcb_dcbbbbdfffffeefgfbaegfedfeeegggfegjihfghlhgjjeeihhhggghiaZTORbpprurmyx]QaXXktklomllmmjijkpomqnmrqonorrsuppqqqpswpoprssrrusqrssstDjzusuutssqrrqqrrrrtuuuvvssutsssstttrrrttqpqpnnqpstsrtutsqmlkllljllmoljopnlkjjigijiiljghhkgcffcehfccefgjddhhcioifhkhiiablrpptsv~wits`gTĜ`Qsztvrnx~|||~xkotru{tloojdfifbcgjjjkkjkgfegikigfjljlomhmrokosrpppqsrsrpmqrrrtrnuvvtrvywsqswxwvutuuwxutsvuuuspqsppqqqpnnqqqrqrqqjmprrponjjigeegiejmklrwxmkmqlfm{wyx||}rjmomjfgkpqqkeimnoroeckqonkebhnpqngcdinoold`irqrohchpoh]PJ>210,JYG3560-+'#&,*(../.6;:==AFIKMQSRWZZZ\a_Z^b][`_Z\XX]]ZXX]]Z^]\`^cVLE6'*.-/3336765987679:;6869<5:PE*#'%*6DTenYKrbFz\]e_X\__babfdfcWTZ^_____```_`_``aaaccb`__`afa_beecc]befddca_bgieabfhdacgieaefggffedeghihhgfegijhhghjjjjhfgjkkjhffgheWQSU_mtusqq{v\T^YYjrjioomkmopnllmopmmnpqporromnorrrpqrsoprrqprtsttrqrtvChxttrsyvuuuwxxuxxuswxvwutvvtssrvwwurpqrvssssqqtpsussuwvstvyzvplpttqsrnmoqrrpoopptpikmjfinnhfhjiigggffhjdgjhghhefbbdfgfhfcbcec^bbciib_gmigZhoq|[T{iGan|wvxqqqsbw{dbo~torpllmjimjhghlmkhmlnqpmnppnostpnqtqrrpqtsnrutuvwurrvxwvwuwwuvxxutzuttwyxyxvvyyuttuuvvvussqsuvvutssrrqqpooopqponoppqqomllkknpoqsngjqxzwrqrt{{{|kmnjvxhqqkjiflrsqidimnpqmggkpmlkedkskppjefmttnkfciqqrohchpohWPOB.*-0JZH3561/)--**,+)-+&()&(&'*+)*/477:=>CHKJSWVTY\[[__ddbb_ba_`a`\\bWLF6'*.,.3655667:767879<<:9:==DRI.&('*5CVdkWNu`H{]_d`WZ[\acfhff`UU]```bcb`_]``abba`_eca`````da_`bdddadecceghcegfdcfigc`ciic\_fifeiigdhlligghfgiijihhchiedgihiiihhhjldTPWZ^jsusuw~v]U^YZjrlkqmmmlnqplkkprooqnronopnmooopqsrokppqrrsrptrrssrtwI{vxwxz}}uw~}xxvtvtuyyvwxutvwwutuwwttvvuvytwxtrstrsrrsvvusswsnrxwuotxxtqsuuprusqrrsopssrsuomqsmmqqnklljlkehgfgecedcbfcdfcfc`bc_^aa[[\[]]Y]ZZZZZWTWWXUTU[ZapnllomkuNu\BOֻ}{ywrolqspmmoolptsqvwrxuutrswvssrtx{yvyz{}~xuyyzvtxyvvvx~{yytuzusvxwy|}|zwvuuuwxyywvuusstuvusprsutsrrronmopponoopqonqtnrwxxwtrl`]db\]bdbejjgkttx}yt|}wroqrkgijmrtskcftmmqmfglntuldglmnmnkfjrtonng_dorqmfbgongYOMA/+,-JZH356201/.-*')/%((,,++%)*(%$&&$#$&%%&)*,04559?ACGMNRVTW]^[__]a^XMF6'*/,03449;986899:=>>DADIKPX\C*"%&*5D[cfUR|bHya`a`WWXYaekogebWRY`cccccba`aa`aabbab`_`bba_`deb`adegfcaaccaffedfiheceffffhiefggfedchgfffggggghijjihiiihhijijjjihijldZSOQaptuuwswt`WbZ[jsnlpnmnpommoonnooqrrpmlopoorpmovumlqrrronortrmox{vrsUy|}tkimqrttvwwwwvwx||vvzvuzxuspsuqptwvuvvxzwqswuqx|xttuuxvwtrxynssttttuurqswywuvwtutqsvsprssroptvsppqnnrpmonjmqmkjmmfehheggdege_a_``]]^[\Z\ZWZ[TWUVSRSRYlvuomoqhWRWZZZVVceaWP\wɽ|xuvuqpsspnquwwtrqx|ysxxy{}yx~|}{}xyxr~wwwy||ywz{zyyyxwxxxyyxvt}xtsuwwvwttvvttvupsqqvtsrvwtruvolt{zwuuskjkljjkijkkjjkkjkfgiedaW[nsrqjhhknrurjfioqommjhkprpkhgioromicelpknldaipnqqfadkpi[QN@0,.1CVI799425332//0,-/.)(+..&)*((*)&'))&%''$(%$')'&'()+-0468??@CHLMLFA92-,-.34579:<93.+("),('0DXdiVNtfNuc_ibWZ][addggheZT[da``a`_`bbabb``a^bcddddeggbaeeddebdffeffcgdcfgeefghfeghggfigfgihfhghijjhghhefkkggiigghiiijjjjihij`TRXZ`lrtttszt^Y^\Zktmmojnnlnonnpmmnnonkpqnntspqqsqoooorqqqrutrqvwussqqs[¹wc[diejijjmnonvuutwz~{yuvxxxywvuuttsssvvvspouzwqtyxrrttwywuuwxvvvuwwurxtyvstsutpqtrpppplklmkkmfiligkldccegha\^_[YVXWT\ormkouvlXQTVUWUVZZZYZ[_a]_]\a_ZZ\^^``VVe]^cdbehekmry~~yyy{}|zxxyy{}}{yy{~~~zxwy{sux{}|zxwwwx{|yuztwzxwstvwurqsuwttvxyx{snnpmmopompuuolmlpphejkfmmnstmjiioqomiggqssqmeemsrpjcdkpsspieejpqrlefknonldcinpfXQN>,,/-GYI3689;9458423311475/-./..//-,-*(''('()'++'%&'%'(+-..,,--.,+,15149?EKOR[\^_^\YVNLHB;4/,***)(%$"&!'*)*3GWfkVOxhJqa]i`W\_^cgikgfd\W\aaaaa`^_bcabca`a`fcabdccddbddbacdfgecefghfdceffefgfggeegggghijhffghjihgggkhijgiljfikhefhhggilljkmdVQST_nuussqys_Y`]\lvopqtpnnopommllnqqpnrorsoqtsmqsrsporqoortutqotvuusru?n~~wqqqqlc^cillmmmmoqrsvyyxz~}xwwxuqsyvssxvqoquuwwtstvyrrwwyzwppzvpwvsstsqnpssnlkoolkmqllmighgbedef`Z^b[USYXSZosomoqodZRUUUWWYTUVY[\ZYWXZYY]_]^`_bfc_ccgiebehfddefffghcegfhklkmqokksurtƱȳ~~{yy{}}|{z{|}z|~}{{}z~~{yzzy~vuxvux|zw{{utuwmtyywwz{qmosrqrssrrtspopsurkfglnnossmhhgkmosqkhmnmkmojgnupmhcfmootrkgekupnmhdhlkmkcekpofXRP?+.1-GYJ67;;><57<946869842331-,-12/.0/-,/.+*,,,*))+)&,,,-./0125:?BFJLVY]a`]YVPNJE>70-))*))'&%%%%&&&&&%#$#(3F[gjUNxgIs_\i_X]a`filjhhe[V[bebaaa`_`cbbceb`cdccddcdfeddddedddfdcfffheddeffefedfgeefdgigghhghijjjgfffjhklffhehilkfhmnggikkklocWQQUborvtsrws^Xga[kvrstpjnrlmrpjsrpsolssqlloqppqoruqpqrpquupptupswxssvw>^gacdee^aefdaabcdedcdgikiknqpruvwz}¿vsyzvuvurprwxuswuyuqsrqsvwuutsrtnnrsmlorjjkigkmekicda^`a_ZWZVRajnkjmoqj[TXZYZXYZ[\\\[\\[Z^_[\^]b`_bhfa`aggbadggjjihggfelklmkhikjjkkljjibgifeggdgeeghkws|z~ɹü|yxxxyz}}xx|zy}|{zxzuwzusqvywwyzxsprutsuuxrrwxstzwxvleiqtqjkonmidimonmjiklmmnmffonmooihihoqmhhilromjebgmmpmgiprrjZRR@,/4.DYM:<==??89?<8:<;<:535766323424784221-.030.00.,,57;@EKORTVZ^`]XSROKE@952.,*'&%%%)*))'&$#(''&&%%%% $%%+4DZbeTMtfLza`l`Z]_`cdgfilgYRZeecbccbbbcbbdeb`dacddddedcddbdedcaddeggegfccghddgeeggfhiefiifhjihjiihhhgggjhglkgijffhhjljkllihiklcUPUZdnpvrsqwr_Ye`\lwpppnkpqmqunrtnmutnpttrrssqrtnptspsutnnqswvrqsxxtsvv:`kbdjkgcfgecefecefecdehgcbeeccfabfgny}v}~~|{uwyu|{wuvy{vursvtpotqlnsvqjolkiehkihmlfcbaa`]WUZUQbornnoopiYT[_^]YXW[]^[[^aZX\`_^`a_`aeggffeiigjlmpklkkmpommkloollpnkknmllomifimmkifjjedhhdb]fdafdgtup{y}}{{|x{{{vy~srw|~|zsqqrruxxxvvyxwvwsxxlbmyuojpsnheghoqoljhgllikmiflppplechkoolhfgjosqg_bimpomgjmook\RRC-/4/AYO>?@?@@;=A?=?@><<=;78<;99997788533358;77;BDEIOUWY[[[YXUQLID?7242.+(''(+)%"!!#$&''&%#"!&%$$$%%&&!"!#+6EYbgYRucL}``ja[\_chfhiijfZT[dcbcddcdgd`becabbabbbcedaaefdcedeedddffgjfdeggedffggeeijggggjkhgjihghijihcgffjihkjghkjjiijlkhfjlleYQRWeqrtprswubZca^owonournnpsropqqonorupqwvonrroootwtrwsrtvwxxvostuwvuw9cm`affccghedgfcccdeffgfffggfdfigdfeddadbbdffegjiimprx{yƜ{oys|uy}|yux}{plmkjlmkkmjgikib__^`[TRZUPbvunnqqqj_WY[Z\\]d^YX\`a_ba`ab`^``cfedfgfkmjkpnknnonmotrnpooppppprpooooqsonooljntqrpkijmormoolllijdfhhgdecddcbcfhljmpomu~|ttwxttutsy|z|}wuxwsy{}rfsykhnrqmghglppnhdelsuqkdfqnomjihhjlmnjeelrssjadjlkmkfgihki]OPD--4/AYQ?AAACA>@B@ACB@?ACA==@8:<=>>=;>@BCEHJITUWWWVXZRQNJD=841-)%$$%&%'+--*'$$#""#$'(&&'''&%$(&%###$$#!,7F\gkZRwbHz_^gb[[_hmjkmjif^Y]ccccdcbcfdabdbaaabdeeddeffcbehdbdfdefffgheggffffdikjhijjkkgfijfgmihghjjjighmnighgdgjjihikfghhijmncZVTVertsostyucZhc_nwprurqonnmovrqrqnosspsqpsrqtqsssusruutssvurpuvssvwvw?hsijigfkjhffgggigefhgdadefgfggglfffghehgghifcdhhggedhidib^bffdefmmmqqqxz|ѿzmŤwxysqolrtkddc^_[X[TSjuqstun^VY[Z\[\X[_`^]]_ab^]bb`caefcagiclkhltpkpuvtrtxvpprqprvvrqsrqqstsmoqtsrsurjkvxolqppqsonspqlwtijmurollmoonsjehhc_`fdbabcccadinrw{~sxz}y{~|zyx{}~~~{{zqwuiinoqqgchmppnjghlrrolggnprnhedfmppqmeelqlmjcblqnnmhhgfkk]MOD,,2/BYQ@ABBEA@BCACECAEECACCB@CDDFLONPV[]]]XRTRME@=71,,*('&%%%&&%##$'%%%$$$$$$$$$$$$$%%&%$"! $#"!!#$%$"(+09AL[hjSKtfHyb`hd[Z^gkeellmi]W]fgdcccbaaddccdb`cfdcddccdehgfffdegdegeeffffghgedeehihhfehhjjhiihikjiiiiihiijkjiihhlkijjikffhkjijmdXVYY`oytptvyubXea_r}urqosusnoqroqrstttuooprqstsprutqsvtturrwwtuzvuvwxxtEhrospnqpsromjlroliknnlnjijjjijmhglngfhe_dhhffghgfddffdcehkiecfieeffgghhdfhhggikehmqolmrtvx|snsy{{{yق|ɒst\e]wtqzurv]XYWX\YY[W^\^_Wb^fe\`ecgejhcefgiiknorsrpuqqutprxttssuvusxsquvssvtuwwuvvronquursvnqrrsrqsppttpmopklnpqqrrrnggopljlmmmmnmlhjiffijhghijjihgjoprx{yploqryx~~}yyvpmmrqiigpolmiekpsnkmgbgopolgcgoromjeelrolgcdjnklpkbcghg]UQE/+32>SQBDGCDABCDEEDDCFJMNOQRWY\^^][YUQKGC>950***'((#"$&'&%%%%$##$#"!%#$'%"#'&% !&"&##$%%$$#''(*+-27<>BCNTLJZghYNzfG{c_ebZZ^gmihghkh[TYbkhbaffb_cddcbbcfddcbbeghcadgdcffgefiifefhffggghgeefggfgieghijhhjdiljgilogikiikllmmmlmlllhjkjhinr`WRU\fptwwwrvvd]he`n|utxsqsvsrsrrwwvvspsutvuprwwtuutttsptvuuwwvxwuvzzvuwAo|ruzwsstttutrqsqprurprvplotvtrlloommppoomkijlmiklkihikjkkjihhieeeeeeeefeeeefeehecdgjifffeeefghgghfceihhffhgfhlklrvrmpw{u|u~xu}kT̎urtwsrnRHq\gstttnid]`]]bfe`febdhfdhmmosssuyrqstsqtyutstwxxvsvwtsuvttopvtpqvwvtssuvutuvutrppsqqstvwvwutuuuroqsppsropmpstsqnmlorqonoomnonmmnojkllkjkjlorpmlmlnrutu{~xxmjorx|mmnkecgnnnojehqrokjgionknnfekoponlghmsokgffjmlpmdbilhd^TOD0-30;NOILJDGCDFIKMMNUTTX\\WRSQNID@<://-+)'%%%"#&%'(#&'''&%$# !"#$%$#" "! $!!$! &#$%),1578=??BIOQSZ^WWYNIXgj[MwhK]^j`XY_gmjlljkh[SYdgdegfffbcecbddcfgcadeeddebdgdcdcgfeeefggdeggehidfiiffiiegilifkmgghjljhiljjjkjiikmknplkmkgjlkhgilf\VTV_ltuutputc]jhcpzuuvrpruttutwtqquwtrtyxqovyutstvwttusuwyyxyxvwwvvwy{Enyqtutxwwyxvuwvwutwzxtuuvxyxutvuwustuwzwuuttsssqstsrqstrqpoponmooooonnnqnllnnliikjhjlidijjjjiiiiilkgfhghgghiiiicedceedecbdddhkhhs~~eeb{iZxpmyxvwj55~η]oĸ{xxlzupvxxyupomptsqptxtuusrstuwuuvxyxwrwyusvyxqtvvxyyxy{yutwxuttvwwwuszxwvxywuyustxxtouyyxxurtstvusqrtnqturnpsrollnppoqlmnlnspoqrpmlmnls{ytt{}og`_aadhfigfhgiq{~qxqnssmggklnppkghrqpmjgjntmljddlppmliefnsqpkeelqmnhbfppga_RMC1.2.ADHMMMNPSX[^\baYV\^[[fk^XWMJYhk[LvgH{d`e`[^afjimkjkh\RW_efcbddcefaeiddhhabdefeefccefbcgjehhffhighgghhihgikjggjifiihjlhgllmljjklkkmmllnnmmmkjkkihljgggjklf[TUYdoussvtzwd]hfervswvsstsqquwtruusuxwywtuxwvzwussy}ztwwyxuuwwxwy{zxxz]~|zsnottpwzxvy|{{zvvy}}{y{~zx{yux|zzzyuwzxvuvxxwvuuvvuuuvtttuuvvvtuwvutuvyvstttuvstuvwwxxqqsspoqqonoqpmkmrqqpnlkikkjlnljmifhllhghhfmdffdebfbb_Z^\\[WWYRR\koonknqdRMRNPdbK`uvsuwkʳyBA~xw~|ww{zz|{wuvytuxvxytu{xtxzxxuvwwwvutwywuvvuyuuwzyvvzytruvuuwoswuroqtrrpossqrqstsrrpmlnuyxw|~zlhligjlnjjlkjkjjlof^egY]hqnooiemknqniijmplijgfmqnljhehopoojegmqpkheehmnnnf_elmmZRPE0+1/;KMBBA92/..,+(&%"&)'%#%'$#####$$"!!"%%&% "###! #""!!!! "#$%',2678:??AHMNRUVTUWZW`ea]\]]^`daY\b```____``ahg]Zac^dfeZYZPL\ikZMwhH|faec[\`hmjkkkkf]Z`eedghffgfecaeiebfeddfedfihhfeghhffgijiggighjhhjkfjmmjjmmkhkkmplimljkmnnmmmkkmonnoomsvpkmmkkigfgkmgYPT_jpovuww|wc]nkfr{tstsvvsssrswwvuvutusvywvywqtwwuuxzywxxxyzz|yzzwx{yuUwz}}kalspsxtsy}xvz}{y{||||zttwx~|y|yuruyyustwxxwtsvxzyvwywwwutwzyu{swztx|tuvwvsuwtxuqquututpnrxqrlloopgbhceZSVWPPYjklqpnmdRPSRRUUWSPQYUT\[\W\\Y]acbgfdgjloklmnnlheijihhijkmggoroosqqppt{}{~xzxuuxzxuwxvssvzwuwwrswvrxytswunptyxtv{|yojotrqsvsorwtptpnqmdhplfimnpmhjlnmmomiglnookggjmllmmihjkmnkfhmnqokfekqqslcclojg]SOF4/3-#$$&'%$(##$%%%$$"'('$#"!!"""### "%&&'*-7;::>BFNNSVTPPV\VZ_^ZWUVQ\db\\^^`bgbY\b_`_]\\^acblk^[eh`cjj\Z[PIZjm\LteH}bdng^]ahmhhhgih_[_eggccfgefdggffhgehgfeffedghgefhgfgijjihjkkhlnikpmmmmklnmknkmomlmnpnllnqpmooonorqoomptrjiokhfhlonlaYTUXbnuvtvv{ub\kkdo{vttyxrpvyvtrtsswwvxtqtywvwvvwxxusvzwuxzxyzwuvwxyz{{8_oggf_^^]]]_bdfdghghlljonnqtvvuqv|}}~|{}{zyxwuvvxzzyx}|{zyy{|{uwvqtzxrquvsnp}xrqtrprvqommkjkf`_^ZXOKYloppntveUNQQSYWSS[WQYa\UYY\__`eldgiihhknponorsrqrolmrtspppmijnnjkmlhffebeegmrsmgmpux{þ~~z{{{zywvry{|qsu~yxprpnruuvmprrssroqrwobkwpflsrmkkiiklnnjhilqrmigimnrojhfhmlmokeflmpmjcakrmmohdgnrkZRNH:-)) &(#"! ""$$! !!!$$"$  %&&)+/51AABFMRUUSTVWUX\^[VVY[[]^[WY^Z\bb\[][ZbicY]cbfklbXZ`^dd]]^^eechf][cgebifZ\[NMYijXKydH|^`na\`bfkinkkni]ZdkiigikifhjgimlhhjiffjihhhfghijiihggfgihgjlljimomjkllknqpknqonolkpqjinponlomnonlmnmnnoppnmqkggfjmkcXRU[dovvuuvxu`^nkfrxqswssrsvutuzvstutuwuqquvwwutxzwuxzw}yzzutx{zxvy}zx}<`j^_cadabcdca`^c__bb^^a\[[\_``_^^____``dbbcdekqlosuwy}~Ԭ}{wyy|vuutzwtyvqtpqxsomghga[XYTNZkqmkmmniSQWWTWX[ZYXUVZ\Y]]_ccbdhhjllmortvtssuutrutrrrssspoosutrrorrpqstrusqoopqqtqoortsrmllmooom`gfchifgje`_cghgmoqtx|Ԯx{vt{}yxzzxyxvvy|zwxvytimuphlopomkiiloonigjmonjigilrrolhehorqkfhjmrsohcdimmmngehmqk_UVK/#&$! "%&$""%(***/56@@CJKQTVYWTW\Y\baZW[Z\bdaZXZ]]_`\XZ_\afd]_a]]ekd[]dbcgicZ^dbcb[[\^fgeki\Y`c^fnl][XJJ\klYKwbH~]`ob\`bgjinjknj^\ekjjhhkighggigeilffhiilkiggikkjihigjifjmlikoollkkmrljlnnmmnpnmonmportpmnpnmmmnnmmojnpnlkkjpnlihhklf[UW\dntxzyy~ybajihu{ruxtsrtutsussuxwttvtrtwvuwyxvxzyxz|{yxwtvzz{{ywzzy|9_mcdd_`cdefeecc`__bbabda``acdcb__^^__^^a`aec^\]abca^]]^_]^bdcdghjlorvxyȦ||vjtwttihgb]YZSKUmnlmlmrnZQSTUYXW\WVYXZ[W][\_``cghkosttrrxwuvxywvvvuutuvwxsqtvttwuxwutwvtuusrqrtvvsqpqqqpuspqqsrqlnpnnqrntpkijkjijhfghigd\]`cegghkmnptzݯ|wtxuz~~xrv}xqoonnmgimoolfdhnpomjhginmmmjfhoopkggjklppjeeikmmnfehlpk[SSK1!% '3AJQOG=97?GHGLNTWXYVSVWZ[^a]Z]_YZbaYUYZ]ad`[Z]^_aa^Z\a^bgf__a^ainf[]bacfid^afehha``_dddmk_\egaflh\]\NL]mmYJvbI}_and\\ahnjklgjkb^elihjlhikkgfhjihfcfjigjkhhijkjhhhihhhjkllmjmmmmnnpopopqrplnonmooopmrsnlopmmrpmqqoqsqnmmpppikjhggild\UTYgswswuruwc_olgt{ttuusstussuuvxxvstwvwvuxzxxusuxyyxvvxwvxyyzz|~~z{zu8`qjlmgggggfghjkdffefghfb`_`bcb``___aaa`Z_b_[Z]`\]__^^_a_[\ab_`d`_`__^_^[Y[_a`behhhijkklpuwx{shhnpsu}~t̴yKFT[[RQLMKEEFJ]ojloilujWMPUX[WUXTVZ[\`ca]\^abeiginrvvusvttuwxwvxxyxxy{|{uswyxy{uwwtuvvtrsuuuuvwrsssrsuwvtttusqoqqsqosvqrppprrqookijmomkpppnmjhghfedccehfecbbcefkknryqv}z{ѣ}tqlwxjrphhigknnlgfiplkoohgnomlkfcgnrnmlhgjmloleehlonmeehkol`UNMC2+0>Qkvw|k\MP\[UVUXY\b`[Y[[Z]a\Y^]YY^^YWZ[\`a_[]b``bc_[]b``eg`\^_`hnf\]cadehf`bfefgcdb`b`ipna]bb\^^WNWZKEYikXKxdI|abkd[]`imjkpiii`]emghlmkkliljhiljjljjiikhgkiijjjjjkhgiklkmojlnllklooonllmnnpponoooonoooommomrplnnlmqnmnoonmkkjjnnllf]VTZfrvvvvstwg`smfpyuuuuttutttvvwvttuussxwtvzxtuuuuuyzvvvw{{uu}zvwxxx{|>eskmojllkjjjmoqilkgfhgdhgefghfebcdedeffdgd^\ab``ab`][[\^_^\[[ZXYYYYYYYY][Z]\ZY\YYYYYYYYZ`_XW[[TXXWX[^\XY[]\ZZ\_]]]afihfkjfhotuw|xdbd41LQD784/.//,=drjlqhnud=5;@DINTgtpbeu~xsppponputrsuvvuxwvwxywvyxvvvxxxvtuy{{yxvyyxx{{yrsuxyywvuwxwuuwzwvtvwyyxtuusquwtttuuutrqzupnprrqoppqqqoooopplilqkkmmmkihkjhgghijinslozvqrx~|z}}vyjfeevwhtshfijmoomifiopppjdfpomlkffkqpmljdejlnpnfaflonleehjnlbYOPRKLYoxy|x`U^`ZYYYVY``ZY^\Z_c^[_[Z[^]ZY[]^ac`^`f`acc`\^cb`eha[]_]ekdZ\b`jikjdcecce``^XXTRUQG??=953./HYOJ[kkYKxdI{ccia\`cfkinolmi]\dikmkjlmiifkihkljiklkkkjikjjlllkkjhjkjgijijigjmnlmmlnpollnnnnljjjjnooopnnqqopqomookmprokikjjkllllkbXRV\fqxyuvxvud_omgrzuuuutuustvvuwwuuwwttuxywwwuwuwxwvy{wuwzyxzzxywwz{{}In{qrtprponmljjjpppnmllkonllmmljfhjiggikefd`bhf`cdeca_`a^^^^_____^][ZXWW[XWYXTTVTTTTTTTSX[[VUYXTPRSQRRPKKMOPPNMLJKLMNNMKOLMNNQTSPQ^bSQXOQVWal]R^YECPSGBGD97;9/9\hcehfnr^2)+,+4BQuü{z{{ywz{zyyyvrxzzwwyywywvwywsnxxwvvvwxxvutvwwvwzwstwxwwwwwwwxyuvwxxvsprstttsrqsqrusnmprssrqnkipppooooosqqkntomqtttxxttte_`^\dkecbdfhnsxvzktysjjstkedjlmmlgdfhqtohehnooqohfikopnhehmollmidfmnnkdehjnla[VUY^l{}x}|nkv|rYYa^Z\^\\a`ZY^[[bha[__`dgd^]`^_ceb^_c`acc`\^cbcgha^_^gnsj^_daea`]SNMIGG@=70/+%'&# #=TNJ]mmYKvbJ~bcjb\`bgjinmkmi^]dhmokhklkkljhggefiikmkikljjlmmkjjjojknlijljlnmlijnkmmkknpnoqqolmnnmnlkpqonqmormmpnnnnnonmlljlkginodYSU[cnuxvwwyvc`pojsxrsrtuvtsvxwuvussuvuxstyzyyxstyzwvwvxwwvx~~vy|ytx}}|Bhwnqtprpqstutsrtqqttqqtusrrrrqonqtrnlorjopkfhmnjkljhfghhbagf__e``______]ZY\[XXZ\[ZXVTRQRRRQQQQPOQQONOPOLKJJLMNNHJLNMLKKMIMOIJLHIJLIHHDEGHJJHHFCFMGCIKIIFFDADB::7/*(%$$ "!!%,' >TOLZjkXKx`K`cld\]ahnjkogfg`]emjkmliloliklifkokjgikhhklhjlkihjmjhikkmnlnnmnnlknqoopponnmoqomnqqprrqpkkpoopoklmjmoqqppmjidgjhkmic[SRWdpttywtyyeasofntrvwtuwtrwzwtsrtwywtuxvsuz{|wwxwvwyxx|zwy{yvxw{3JRLORSVSSUVXZ[\^]\^`bbaccdfhijkgijlmmmmllmnopqqoqsuuutsqppoonnnkkjhgeddgfdba_]\^^]]]\\\[ZYXVUTSTTTTTTTTLLLLLLMLNMLLKJIIJJJJKKKKHHIIIIIIGGGGGGGGGFEEEDB@F>?AA<9EV]Z^^^g\F=FGBFDAEIQZbimowx~¾z{~~xz~|y{wqqrtw{}{upwwwwwxxxursvwtsvutsqqrtutuuronqtosvtqopsprrttvwlmlv~vrw{tifkkiihgjlkkklkhjle^cg`^hopomhflpnjljfgqmmnkhhjlpqlfekpmkjigimnpnlhffjmrpe]dnolh`X`nppzykusejp{t]X`Y^`^`iia`d]]fid___]diaXZ_adikhda`bcefaYUVQOJD=73032-$!  !"##&(# (+)*-1%:VPJ]mjVLxdI}bbje[cecmqnnpmg_^glklliijlmlmiinnjkkkjjkmmlkljgkpoijljhlnllnjlppoolkmopnlkkpmmpqnmnpmopmmqtonqromnlnsrnopnmjeglkjlng[WXZanuuuupuvf_rlct{vsttuuwyvsvuuuuuuuusvz{{vuxwvuwzyvwyzxuuuwyw|xt+AF===;=>>>??@@ACBACEFFECCDEFGHIIJKLMMMMPQQRSTUUWY[^_``_ccdefghhgghijkklonnmmllkkkjigfeeccbba`__\\\]\\\\VVUTSRQQRRQPOONNLLKJIHGGIHGGEEDDFFFFFFFFDCCDEFDCA>>??:9HfkcgigleP>BEAFFD@ACEFFFEFFGJIHINRSW[^acdompy}~ʳų~|xutvz|urqrsqrvtuwxxwvtruwxvuttvtrsuvspuysnrvqhft|xtuxyunptpmnnrpmlmmlkmmkc[enjelommkgfjnooogcisojmogcjnnmjffimpopmiilnkmlggknlnogagnmjpfYd|{mmsmvwfdkm{gbf]]_]`hg`^_\_gha\]b`gnia_^]^^\UNIGB@?>7/**&%$" !!!!"#$&&'**)(&%$$%%!$,,)+,0$8UNH[ljWN{dI|cbje]`dgklqpooma^hkmnlkkljgjjhgjmjghjighkkhmhgkmkkmlmkilooqojjmnoomkmmmnqpnqnmqqnmpmooqtropqoopnmnnrqnmnmklnkkljjmme[WY[erxvtvw{uddrlct|xuvuutuvttvsvyxvttuutvyxxvtvxxy{yutwz{yvvx{upcXV&@???>==<<:98:<==;::;;<<<<9:;<<;;:9::;<<==;<<==<;:DDDEEEFFHIJKMNPPSTTUVVWWUUVWYZ[[^^^^^^^^________``_^^\\[XXXWVUUUTTSRQPPOONNLLJJIJJKJKJJJGFFFFECA??>;<65HflacjeggV<=@AAABCCBAAEDEEC?@DAAABCDFGGFFFFEEFNNOOQTXZZcigjqsow{zrqzɵͽ}zwuyxwutqmjsrrrrqqqtwtszwoniuvruvtwuopqmmpnnoprsromlori_grmgjmmjjkiiljhlkghoqpnkedmomljgehlpoomijnpmmlhcchojmjfjmkioibj}shmou}xf_ik{tee^[]]ahga_b_agf^WVXTUUNGA:6773/*('!  !!"#%&&'))',++*)(('(((''&&&%$#%)&&++/#7SMG\mkXP|dI|cbke^_cjjhrsmml^[hkkkkklnmkigikigikjgghihhijmmihlkglmlklmnpnmoqommmjknpnjloooomnppnrrpostqprqppqpoqqomoqnllmkjihjnmd[XWYdquytvw{ufgqjarzwvwvttuutuursuuttuwwwyywvvttvwwyzyxxw{{wy|ndb_]^_$8=79<9:==<<;::9;:9:<=<;<<;;;;;;<==>=<;;;;<<===>@@?>=<::99999999:;::9:997889:;;;???@@@AA?@@AABBCFFFGFGFFHIIJJJJKNNMMLLKKKLLLMMNNMNMNNNNNMMMMMMMMMLKJJHFDFFB=>85G]jb`hfglY=9D@<@D@@@@@@@?@?@CA?AECB@??@ACEFEDCDCBDEEDBCEHABCEEDCDDGHIGHIKIORXfkb[[dkvz¶|}}{yxy{xwz|{wmlu~}tpmlmsvtostqoopppswn`htqbfqtljjbjqqnnkggnoqojhjmknqne`hsrolifhlmlmkedimnimkgjljjikpomxzjjmrwreajozugebaa`aa^WSRMJKH@851-,*$#""% !#$$&'''(()))++)(()++((''&&&&&&%%$$###&''((+.,1$7TMG_olYNzcH|ccke^_cgjipolmm`\fimnmkiklmlkmnjfhmlhhllhimijjjjlljjmnnnmmnmmnonnopkjlqqonqosqlmssnqomlkproqqooqpnpqqpqrqnlkjiihkmnb[WVXervwutryxhdpkdtzvtwvtvxvwwuvtrruvvtvxvuxywvvxwwwxyzxyww~~ul_[adhs&:=69;7788889899:878:;:988877666:;;<;;:9<<<=====:::999::6789:<==A@@>=<;:<<<<<;<;9999::;;7889:;<<99:9:9996677889988777766566788999999:9::>>?>?>?>BBAACB@>?>9573/@DBA@>>?@A>?@?ABCB?>>>>????>?@BAAAFCBBA>?C?DDBCCBBADBDB?GLGDKLOX]bz¿y{zx~}zznusoswvwsuurqrrppovqditofiopkijhjnopqjfjnmopjegnolkkigiopmlighlmllgabimmkmjfikjllowshpwmgiqumeehiu|~q[USKHEB=60+)&$#! ! !%##$%$#$')/)%&)(()++*)(''&(&%%$$$&##$$&&''##%&()*+,0348>:0/2&9UNH]mkVMxcH|cclf\bcbjmkmookc_hllmmjjkmmkomimolknmlmlkkmkhhmomlnonljikorqommoqpnnpokklljjjiefffdhghfeinmtsqoqonqtsrqommkllkkmlmnf^ZXZesvsturyyg`qokz}utvsqsttuvsvutvxywuuwvtuvuuxwwxwwxyzyv{|njc^n~}{(;?:@DA@;;<==???=;:;==<;==<<<<<<99:;;;:98899999965433334333333440000000033210/../012345623344556::::::::;;::9:99;;;;;;::==<<<<;;;;:9876655555555320100-+.+*)*)'.GdldfgfkmN018789:::;=?AB?>@CA?AEHGGGFGHHEEDEEDCDECABBB@>>B@;;:=<:;?9;====>>@@=AA=BCCCGB<>>EDJIFHHINNNRZhv}wtty丆|{{~tvyxvvuqxxjmxvqnmqtmfgmolkmicdjpqnhbdnlmmkfcholmnmiimosoicbeknmmgeikklpopruwupdhqtlfeeejws>%)&###"#"%&$(*&&)(+++)&%&(*&$&&&&)%%%%%%%%!!#'(%$$)**,-./0:;>BEIKMPPQSY^N504&9UNHYjhVLybG{ddlf\cd`jnilpmia`hmmnlkklkhjolimnklpnmnliillmmmnomkonopmkkjikjhhgeebfgebdefjjiijlkjnpmkkjhhmnopqpoqqopqnkjkijjkmllnaYVX[guzutvuyvfdnlgw{wx~utsstwxvtuwvuuwystwwsuxxvstwwx{|yqy~pig`^vu&8;59=87667899::=;:;<=<:==>>>>>>:;<>>>>>=========<:87778<<;976545566778833210//.00/.,+**//000000000000005444444488888888;;;;;;;;>==;;:98:9:9:9:9;986630.)&,,(((,>ZjgiifjoZ4(.++-210../010025536:78899887=::>?;<@AAABCDCBDGE@BHG@BACEEA>=>==>>>?>@@=BA;<;>9:9?C=>CC>>A@?AEGHGCCDEFFFFKHHLOOSWY\bhnrtvുzpʾxrsqztt{iosleffproolggkqpkigfknookfdhmmnomigijmniackmholfekljkpnjn{zpgorojdadgcmy<#'"" " '&%%'*-.,$#(&$$$)*+)'%&'(&&''$&*"#$&(*+,//4:=;;>>>========<<::;<>@>>=<<;::========;;;:9998777666556666555511111111.//12344222223330123456756666666555555556544431/+(22(')+4MbfijehnfA03.0675310011101320151234420/5006612713454445;758;<<;::=AB@=;FCCCABEF@BAGF@A?CBCACD>@:@BA?96==<;=?@?=B@>;;<>@?=>BCAACA@???ACDEHJIHJOTTY^`bgqymVXX[ddrt~|zteͽީ|spköeoqlkgknmnqkfhmnrqjfjpprrlfehllkjiffggjkjedinookefmnjiqqnktklxrhhb^gedctx^LHUKE;-()%(&(4GVZW@0),+(()%'((&$$$$%(++),1568;>ACDEBBED@@B\\\[ZZYYYZZ[\]^^`ZY[[\R?.2$7RKE_pmYOzaG{ddmf^_bggenplnna]hklnmjhikkkjjkijjjijkkjkmnnmmmidbcfaacbbdcdda`gjgeghhgffhigcdkliilmdahqvvqikqtpnnnppponqpjkolfhllleZVZ\es{wtutywe`rmfv|vrsuwtruvsrvvvuuuvvvusuwustvvwxxywr|qryqt{̷&:@::;;>==>==<::9;;::==;===<<<;;;><;=:79=;99;;97<::<<::<988:967;<:89;<;9;;::9988987666667655557777777777766799758998544555656565887766657996688535899642022//.*%+BelggeiqfD,48103467876586568987;88;:88:876667895555666643333333332124422235668<;<>?@AAADEFEDEGIEEGIHFFIDEEDBABD@A@>>@?>?==?>;<@@=<=@A?<==<;>AA?=?@?><<<;;=:79:7ACDEEBBBA?;8621.-.&&=VZ[]YVYWW]]UKEFLY^ZZ_ZL9*2*6SOEVplVLx]Hbcnc_dd`ciqpnqpb\gmlnonllmnmkikolhjljmolkljkliddeef`acefhihgcdjllmnps{{wtrmlppkmuoqqopqmgpkhjljijfXSSZhryzzvxxtefmofsyutwtuwyxuvywvvvvxxvtuwwvuuuwqyxxysxukǠۀ!3:6764645678888;77<<99=;==::<<9;><;=;8:>>===>=<;<;;:;:::;<;::;;9888999899::;;<<;;:999::98776789666666668886558:5677667966666666877766654664355245676532455322.)'AakijhdphI./5211233443334555556655665567899987644567899888999::8::99;<;;9997457;9754689986557:=88888:<=:>??=<::>?><=:;;83550:UjhbgnooW?>>=====<;::;==>:<<:;<<9<:88:;:9;879:9:<<:;==:;>@AA?BFEADEEDCCCBB>?DFCCEBBCEJMF6:VkibekmiN<:<:84:987764224/WqoXLx`Gfdji]^_chijmkmne^gkomllnnmkkjjiiknnllmkikopk`]baXTWg~¾ȷ}nprrprnnqrnllkmlikopmfXUU[gpvwytuyzicttjuzsqtuvvttuwvrqsvvvvsuvwwwxz{{{y{|vwzy|ʹʩ퀀%48676268887643247644774799768867889998835775579688779977::979998:;;989;:::998887777789:::9:9:::;;;;;;;;:99;<<:79:;;99:;::9:9:::777666666766565565332334232001.*06Jhldjkin\;)063432223562344334658866997;:876677899::;;;99::;<<=<>=:89:9667888:=999:9::;?>=>@A@>?@@@BCB?EDCCCCBA>?AB@>?B@>>>=;=?=<::;><9::::;;9798:>?=>CAADJRWH11RkmjkmlhYQTUMFEHB?CDBBGE?9@\Y@Onlkopkedkkhkokfjmlllhbelpomkedhonkjjfcgnmlhcaglklnjeji\Z`^^XJED>4@NV[][XYY\bdaWPMEFPZepwwikpxa:$#&%&'''%%&''&&%+)'&%&'(((&'+%&?USJMRKOZN3).)&)(*':W[XQ<)1)0MKB\rkVLzbIeend]accimsmjmpc]fjikllkklnmkjhglokhklkloniefjfUSnÿԾ{trptpnossplkklkjknnmg\YXZfoxwxuuyxgcrtjvzutywvuwwvwyuvwutwvqvwwvtsrrxvzyqrxx˪퀀&594665966766543623562254223310243566554458765797679:9:;8899967:79;:879;8877665598889:;;99999999;;;;;;;;><:9;<=<;<==;;<=<<<<<<<<99::::::87777666764223452320120,05Ihleljhpb?-17365433568765689878;;9:==:<;:998:::::;<<==;;;;;;;;9::9<;;;:9:=;;;::::::86679:9=:99;;<<:<>??=<<>BDCBBDEDDEFECBCBBCA@????>>??>=<;;<;97898:;:@G?+3WpoihjniVKMPLIGDCBBFJMMPNPM[aLIQconomhbfjmmmfdkpnopkefjknpme`emnjijjgjnomidbgkjjjecojUMRNMPOF=:INXekkhfnlntvroneZVZ_ehdkx~{unp|e?"''"$,+$''(((('&('%$$%&'*)((+$%>\SHKNMQQ?35<6+))+&,EZ]P8'1+1NOJ^rkVKv`Jcepb]cdbfmtrokje_ellkjkmmmkmmmjgklgikjimpkbaghZUr˼wrwsrqomoqqnljjkkikmeWTTZfouttrtuq``tsht|xvytsuwwuvxxwwvuvvspswyzyxx{xztzxxٹΨꀀ2FLFEGGJEEEDCBA@==<=<<;<:6599448546755645666566753377669:89;:78<::;;:9:;7778899:887778997889998899999999:<<<::;=9;<<<<=?<<<<<<<<<<<<<===<;:<;:9;<;98899:67644752+8Sjjkmdhq_>/45254444566:878:<:89::::<=<;<=>>><<==<<<<;;<<<<<;;;=<:9:=<:<;;<;9;=<<;<;<<=<<;;<<<<=;:<;:9::;<;99:;;<;89<<;;;<=<<<====>>??@=>?@?=>@==<=@CDCCILNU[J.0SjkkmmphPEF@=<688646<=<<;>DZ^B/?^qolmkgjpqqnfcjolnojdfkmoolgegjmnlhdgikpmjechkjloe`szaNMKJPUPOZedefdchorljotuw{sup^VemcY`kw|xuypB(")$$)$&&())*))%&%&')**''%'*$&>YWRTURL=/457;8-'+)$8WbO3(2,2PRM\snZJr]Iddne^ab`ehmqnllc]ekmnonkkkmlnnifhkilmkjmlf_f\\gyҽounoqpopsqmljiljhingVPS^msvyzx{{veeyufs|zuuquwvtvvvwvvutwwtwxyyyyz{tx|qryxѸש뀀6Q\SRTTTUUUUTRQPORRNNQQMNKKNMJJLHFHIFEGCA@??@@?>:999:99988998789876778764455667788766677889::9888888888869;<:8898:;;::;<;;;;;;;;99::;;;;=;:<<99;;;:9999977645774);^lhmmbjoX6/4423455665588889999878::::<:;<>>>>=::;<=>>????@@@@@>?><=??>@???><=?><:99;>@?ABCBA@@?@BCCBBB>??@????>=<<=<:7??><;;<=;:99;=<:89:;:9;>:88;>@ACKLJISZF%1RgilmiifTNPKEDB@=:9<@DFKD^WKMRI9+'+,,2=:-%)&2N[P9-2)3SRJ[ro]NtbKdfqjbccdhjnolqrc]hlpnlkmlkhglmkjjjjiklnnhcefZo¾¿ſϢyponoqrtspljjkkjjlohZWZblprvxvxzwebzvgs{xttswzwttvwwwxwvxxtwwvuttvxp{}rpt׷Ӥ怀1Q`WTWUSSTUWXYYYZXWYXVVWXXXXWWVWWTVWSSTPVTRRTTRPNPPLKMLIGJKHGIJICA@ACCB@A@@@?>>><<;:999:89:;;:988888888897557887:;;976669999999955666777854763363333221144312541-<<<<<===;<<=>??@=@@?>?AB>==>=<=@AAA@?=<;>@@?>=?@ACCA?@AAEDCDDDB@CA@AA??@>@A@@???=@@=<=>=?====<=?;99==;>DCHJJSYE$+Qhhfgipl^NKNF>A>>;8;CGFFHBI\^I3@aurnlhgonjlplgkmkopgaekhnqlgillklkfdfikmkheejnlplcdshI?BDO\bcefbdfhjmnmmmmnqtvwwy||iaX]dfrc;-*)&!''())(('&'()('%$((&'*#$=\YQVT9(*(#,+ /?4&*!(F_V812%4TPE]qm^SzbIgfmf^abbgjnllllc]dlpnmmnnlkdijjmmjkommmi`bn_Z}ĽʷwvsomqrqpqgkmkjmpqfZWVXdoyuwuvzzgavvjtyutxwxyxtpsxuprxyvuwruxzzzyy{|uty|ִͥ倀-GNGJLIKKKLLMMNNRQPPPQSTRTUSPPSVVYYVVYYVUXWUUYZXWYZXX[\ZXXXXXXXXSSTTUUUVSSRRQPPPOONMLKKJDIKHEEECFCBCB>=?DA?>??>=<;=>=?AA?=<<==;;=<;??<>A><;:<:;>><;<9;=>?>=?AAABEAADFDBABBAACDC@BCCA>=>@A@@A?==?GBFLPSD'(Ihjeeejj]OPMA@@;7349?BBA=9AZ`C,:arnqndclommmieiljmngchmkoqkdchklmkfeikkpkgecgmoloedgTBALW_dd^^gd_`fjpsnnkmstrtzw{z|zi[\ckvpQ9.& (')+)%&,#')(''('%(('*%&?:<@=;;;::::987623553346,/Qllkkjfm^B56786679<=<:9;;88;;9;;;;<<<<>>===<<<>==<;:98;:9:<=<;<:9;>?><>=;:<::=<8:<;;<=>>;77:=<9:;<;;99AA?=;;<=;;<=><<><97:<=<==<;;<=<9>??=;;>=<>B@DKMQ[N-)Hkrkkjml_QROEDD=;9:Y\^X?((+#(++4;8<4(%%9ZS6-6+5QLE^qkZPy_Hgfli\^caflrpomld_gnikmnnlllmjjmmkjlgknkbiug[Žþά~mvrprrommpmlnopopdZWZ^frx}ttu{yifrpgt{yxwsuvwtuwxvvwwwwxx|uotzxtuvzrq{ٹťꀀ7QXQUVSUUTSRPONNMNOQQPONLKJJKKKLLJIIIIJLGHJLLIGFHFFHHFEHJKKKJJKKMMMMNNNNMNOPRSTUUUVWYYZ[\YX[]][Y[[[[\^^\]^_]Z[]`_]^__[YZ^[ZZYWVXWWXVUSTTTOQRPOQQKKKKKIHGFFGECABB34SnmggkemnW;362333577647898899788888999:::99988<<<<<;;;<::;<=<;===<<;==>=::;:9=;8:<;;<==>=::=>=<<<<<<<<=;:<>>;8=<<<;;>A@=;<====<;;;<>=;;=?><999;:<>?>@DEJOLN[R4$=dogfijobSQMA?>:999;>DHEC@E[^C.:[ponmhclonllgchpoopkcckmmomfcinnmiddgjjliihegigjgcs|eU\fdca_bffcgd^ajqrrolmswuquuxyz{{ywoc_qr[3%$*)'*'&*+'&()(#&'&(#$><<<;;;::;;;;<<<<;=>=;:;=8:;<::;><<:;=;9=:8:><;;:878<<:9:::9::;<=<<;<>>=;99:;;9:<;;;=>>=>??=;<<<;;<=::=@@@ADGINMOWM1';aoegmoqdUSNECB;::;=AEHEECI\`E29]spmkgfiljkmjgjkpqnjeekllmkdchmmoniefjnlqkabhkloiap|m_b[_ggbdgcbb]^cflwsnmswuqotx{{|{lYXn{vyf='*(&()")+(&))'%&%%*%%:X`XE/&*(*),3?;,,<0*&5XZ9%0&6URM_rkZPyhKeeoi_cd`ekoqsqmdaimlmljknmihlmjhilmkqp`cqjbǿľǡ}usrssqnlnlmonkmrd\YZ^htyxy|wyvgf|vgrwuuvtrsuuwvsssuwxwuryzxwyxvv~qlyѱͨꀀ6PXQUUPQRRSTUVVWSSRRRSTURUWVTRTVTTVZZVTTYVUWWTTVUXXUUXXUUUUUUTUUTTTSTSTSTTTSSRRRQQQOONMMPNOPNJKOLQSOLMOPLKKMOONMNLLNPPRUSSSRRTUUVX[ZYX[]_\]\Z\_]``aaa`_^```abbccdWT[hpjejfgfefhdfeca`_``Z[\^^\[ZYYYXXWWWQQQPPPPOOONMLKJJGHIGECDEA@@BDCA>>?>?@=:<:7:=;998:7687669::989:;<8;?>;:<;:;;:=ABCHHKVL/*=cqijprqeUQMDCA<:8:@DGGLJHL_`E2:atonkegjkijnkgikmlkjfdimnnicchkjjifeeghinh`djjipj^h}zk_X_ikedeabeb^dintqnmoponosxzz|x^GRk{{{l=&,+&+*$#()$$()(&&-*(;X`T=-)*#',79:920:3*%6[^8(2(6SOJ`rlZOx_E}bdqh`ed`eloqpnkc`gkmlkjlmkiojilonkigliZjtgyźżŽȫwurpqqpnnlmnnknrc]ZZ]iuxvy{tyyifxpdu|vsuxxwvwvuuzwtrsuwwuxxwxvssymm|ٶĦ耀6QYTWYTUTTTUUUUVTSRQQSUVTTTUUUTSVUTTTTUVVTSSSRSUXVUXWTTWWTQSVXURVVUVUUUUVVVVWWWWWXWXWXWWZZZYVTUXSWYVRQSTVSQQTVVURQQSROMNPQPNMNOMNOOOMMMNPOPPMQSPSTTUUUTSUTSUVWVUWSQSbngbabehe_\_`bddcbbbfdeggedfccdeffggddddcccba```__^^`^\\]][ZZZZ[ZYVTSSRSSOLNPLLLIFGFEFC?=?@?@@>=<<<<;<>??>==A=;;<;;AFILILZR3&>bpjkllodSMHA?<<97:@FGFGEBH\`C/;/**/63'%<]\6'2(6TPK`slZOx]Fcdnb^egbhnprpmleagmpnllkjijmnppnlkkqme`pnjľľÿļʲwvroorqomljjlnmlb]ZY\iuwvxytywjozobu|uru~|uruvsuwwvvvuttrtwzzuu|{lpسŦ怀3NXTYZVWVVVVVVVVTTTTTUUVWWWXYXWVZYVSSVYZUVWVWXWVZWVXWTSUWUTTVWVTWWWVVVUUUUVVWXXYXXXXYYZZ^^][Z[\\[YXYYWVWZ[\]^]\Z^\]``]\]\]\YY[[Y[ZZZ[[ZYWX[ZX[]XTTUUUUTTWUSSTSQOUOFDWgc_imsrcPMTRTWWVUUVXUTVVTUXZZ[\\]^^a```____bbbcccddfdcdffedbdggedeggfdcebbehdcd`^^]_`_\[\[YWVUSRQQPTPLKLMKIIGGGFDBBBCB@?@A@>><9899998655677668;;:;=BFMLMVK-(DfolmkklbRLIDDA<;;>CGIJDA>E^bG1<^oknmeclomlmieijmpmgeimnnmiddilgkmjecioklojcinjongr~f]`djnmkljhheabionrqljnqotzzy|~}~rZTeyU*")%)+)'3A=-$''&)$%<[cQ7+*+$*8>/&))*63$(E_W4&0'6TQLaslZOxbLhgme`ec^gpspnmld_gnmllnoljipmhhmrpjoma`mgr¿¼ĿѺvvtpqsrmnpnkmrrma]YW[iuvzwur{yjnscrytsvyvrswxusyywvuvwx{wtx{xuy|lsδɦ倀7R[VYZUVVVVWWXXYUVWXYXWVSVYYVUUWSWYXXYWSSWZYY[ZVTVWUUXXVVY\ZWVX[XXXWWWVVWWWXXXXXYYYYYYYY[[YWX]]Y^XUY\[XWYZ[ZXXY[_\[\[Z[^[^^[\`a_^]\^acb`Y[^^\_`Z_`aaaa```_]]__]\`_XQ\iimplpqcVW]WXXVSSUWXTSUTSTXQQQQQQQQSSSSRRRRTTTSRQQQTVWXWXY[\[YZ[\\[b`]]``bhb`dhgfgfiggiighjjjjihgffdddcaaabca`_\ZZ]Z\\ZXWVTWWTQOOONLLKJHGEDGEDCA?@CHHNQPSJ5,IgkkmkmkaPIE@@<78:<>BEHGB>E]bD-:[nlkkgckppnlgekqlmoiehlomkicbgklkjihilomihgegkkqg]pk^_^aggipvssndclrlmmmlklmq{~{zz{{r_\eyrO3)*/',L`N.&,.,,%)C\eR6)(,)5:5+,+%-95#-N`T5*4)5RNHaslZOxbJkkpebhf`fmnppome`ipqnlmnmjgjijlnlkjon^gtezſ¿Կtvusstqkiopjinpm`\YWZhtux{|vzvfk{rcrzxwxvtvwuvwuuvwwvuvwyyvsuvvymixyç怀9TUOTUUTTUWTXXRVURRVWUTURWXUSVYYUUVVVVVVXVUVXYXUYYXXWWVVVVWWXYZZ]WUVWUUWXVVXXVW[ZVY[Y[\WTWYYWVWX[^_ZVY[VY[\[XWXYZ^_\ZZYV[\]]]]\[`_]]][_ha`_]]]]][afe`]`dZ[]^`bcd`i_RZfikjmnkgeeg``````aaaaa`_^^^^^^^^^^^[[[ZZZZZ[TQRTUUSQQQSUUSQQRRSRRQPSRQQQQRRUUUUUUUUYZZ[\]]^aaaaaaaaedcbbcdejjiihggglllkkkjjiihhggffeedcbbaa_^][YXWVZVTY_^VM@MfmjolimaVPLFBA=>@BDFHHHICFY`K0:Wmolmjcmprsphdfnmmlhfhlrokhhhgflnkffhkoiffc_dlmtk^oxiebadhjksutohfkpkkmpnhjprz{vw{wojjze7'',*"4YcF 'AC0 "?]bN8.)+,;<.#*)&.;1$6X`O715)4TTL\poZP}aJgipibfeaiprrqokc]cnkomknqmhkimpmllgplZlznƼľÿбsqppqqpnmmkimstqeWW]WWktv{wxvijyqew{xyuwywvyzuvvustwwvutquurz~rn}s~耀Bjwplgin_\`]WTQYWYZZXVW[XWUTUXWUXWWWVVVVWVVWXYYXVVVUUUTTWWWWVVVUTUUWXZXU]YVWY[]_ZWWXUY__]YUUVXXWY_a]XZ[YZZ[[Z[[\Y]^\[]\Y_````__^Z[\]][[]\[[[[\]][_ba]Z[]baaa``__[c^Yaknrkhfec`adccccccccccccbbbbaabcdeef\\]_`bcdba`ce`^edcbbbba`cb`_]\\\WXZ\[[YXTTTTTTTTRRRRRRQQSSSSSSSSXWVUUVWXUVWXYZ[\[\]^`abcfffeedddddeffghhggghijjkjigffgffZWfnmmjmqg__`_^^XXWVVUTTYWMJX`O;AUlrlklhiiijkhdfponnlhgilnlgdfiigkmhchnlkjhecgklokbrnicbdinpkorqmlossrsvrljltyzwy~|~ohp|zG'#) ;[Z@, )E@*$)9XdX?*'4;=0#%,+*0@,(CZ]Q715)4USK]qpZP}bIghqg`ed`gnpopqmcbilomjjnonookkkhknmpi]otmͶwusrrqomonlhjnnkcXX[V]p}sx{w|vfj~vhw|vuxtvvuutsruvvwxxwvvvswwt{mh}u退Zstxspmnsqmkmpolfgigc`chdcba``abccba^\\\^^^^]]]]YYXVUTSRWXVRUZ[XSUVUSUWYRS[]XVXVWWXZ\[XUV[\ZZ\^_[[[[^^^^[^_]^`_\___^^]]\^___``_\^^_`abbc]^```^^^aa`^]\[[\_[Yafinsnkkhb`a________\\]]]]^^`aabbccc``abdeff^ea]ega_`abccbccacefgfedccccdeffffffffffgffedcba````````]\[ZZ[\]YYYXXXXXQQRTUWXXZZZYYXXWTUVVWXYYXYZ[]_``ba`][\_b[Wellnnsnf`bfgghhggggfffdh`V[cd`\^jqljkelnnpqlhhnnmjhhjjmlkidbfmolifchlkllhdfikmijeoyohghklgmuxvssssrtxzwutvxyyz}~}viis`<))/=5)'*))06:&2U\XR904)5TSJ]qpZP|bHfhrg`ec_gnpspqlbdmnomiilllnmjkkjlonoi^nrrĿ˻tsrrttsrrqnkknnlb[YWVcu{uxxwydj|sgw~xuwsvuuwurtvuvwxurpwxuxxu|njꀀZtt~{{z{x{}||z|{yy|zywvvx{|}~}{{{{zzzzxxvvtsrrlmmllnkgdfgfipvxnt~wpnjghhfbabec`VQUYXY[YXYZ\ZYY\[ZZ]ZVXXYZ[]_`a_^]\ab\``abbbaa[\]_aba_______^_e`[^ekmpmopnhb^]aaaa```__``aabbb``__^]]\ccba``__X_^]__]a`bddddegeeedca_]`_^^_acdbbbbbbbbgffeedddgggggggghgfeefghffedcbaabbaaa`````__^^]][ZYYXWVVVVVWWWWWVUSSTUTSCMbebjkkh_YZ[ZXXZZ[\]^__[b_X\dgheagpmjgchmoolhdejlnmiegjqmjjgdfkqoifhiilomhbbfiighcgzvilf_[[]_`hmqutuvywusvyyvsyxy{|||~s`fyva<*4GW^V@3+CP6%%(@\a[H21>:*+-+&*14.,D`\VS;/4)4TRI]qp[P|bHfgrhafdahoqtpqofcilmpomoqomjjkmmnnnmj\kwz¿ͺwusrsrpnonmlmoqqe]XWXgwyzwvwzeizpcu|xwvwwwxvst}ywxxvuuuxuxwu{|po耀]mt{vz}z}~|{}}z{~}|{}xltuomponmmlkjhhgdefb\a`_^^_ab]Z`d`ab[``aa`][Y\[[\]\ZX^^^^^^^^b\[`gjjfhnqjb__^``__^]]]bbbbcccc``______aa`_^]\\_ab```bbccc`]\\^_acdc`[YXY[]^]\[___________``aab````````cba``abcabcdeffghggggfffiiihhggfihhgfeeddddccbaaa_^^````KUjlhljhd\VWXVSSVVVVUUUTTTOO\aWLSZgpqkggimnkkhgjpnprkcckmomhfiifjomhghkmmkhc_ekijhcdmjXF?<::Z_]P916/(*('+-.06CT^ZZU;.3)5TRH^rp[P|bIfhqhaed`hoqrqpmgdglnqqpqpljkkkkmnopli^oy~¹¿¿ƿǿ϶usqqqqomkjjjkmorj]XXZgvw|www|wgktds|xvwuuwwvvvswttwxwutswuvvtzyipľ䀀\kp{uw{{y{|{x|~{z}zyvvy~~|xuxyywsolklkjihgfeabimjljcknmicaacddcba``____^^]]]]]^^_`aa]]^^^__`ba_]_hkfbdeda]ZXTVY\`cde]]]^]^]]]]]]]]]]ZZ[\]^__________a`_^^_`a^^____``[[\^_`abccbbaaa`bbbddeffaabccdeeffecbdgj_\jsqqnqleadhgeedddcbbaaa^VVaf]UV^gpsngflonllkhgmoolhfgihlmighihkjkjfgkjihheaiolnifgf[G81/,**(&#.5@MXaehwyyy|}z{{{{}}}yeep~{a<5_dA"*68WY7!%>Z^\WE50*''&*+).=P[\XX^V:-2(5TQG_rq[P|aJhipf_db^fmonsqhdfjnnmlopkilillmoolnmecvu{úĿϵrpopqrqonmlmlknrl]WY[gtxzw{xxshm}uhvzttxtsuvtvxutssvywtqrwvvutyvdrųÿ Xps~{~}~|}~~{~|ywz׼¾uihaidk~~}}|vutrqonnjjihgfedaabbbbcc]]`fg`\_fgijkiebd`ZVW\dhba_]\\^^^^^^^^^^^^______________dcbaabcdba`_^]\\[\]^`abb___^^]]]]]^_`aab[\]_`bcda```abcc[Vdkkmmrib_beca`ddefghhijjd_chjldaempmhcmqpnomifekokhiigjkmlhdgmnhhkjiihlkidcjnjnhgh_M>72.+)(('%*(')2?LS`hpswz{wy}~zz{~t\_m}l9(FcY7$9<-N];!'AV^`_P714*',/),AT`aZWY^T9,1)6TPF_sq[P{_Kikof_cb^fmokqslefjklkloqnmrjnnnqoklrfcwszķ¿Ǽͺwurrqqnlqooomknsj\WX[gvywy{vxtfkysiwzstyxvuutttuywtsuwvsryxwvvzvfvԽ瀀^px|{~~~}yz|{~{wux~ؿμ¿Ϫ|owwkcyÿrba_^dliggilmnmliea^[Zffffdb`_^^^^^^^^^^]\[ZZY________`_^]]^_`^^^_``aa`____^^^\\\[[ZZZa``_^]\\^^_`abcdea`fmng^V]nmhoqrslfggc^\]]^_`bcc`b\Y]`_``ZbmliieioolkllmjjnpkdfonoolgfimlmhehjlonmgacjlkleefV@67/+&$%')))&#"%)-/4DU`hpspxyyz~~~rd^k}~~s=-LcQ/#?7&G^E%#@\\W^\E772+./*=X_`ZVZZYQ9+1(6TPF_sq[P{^Ljlng`ec`gnpqptodbjmoqpoolijoqlilllqwi_ru{ʽ½;̿utrrstrpmllmlimrg\WWYhwyw{ys|xeh|tgtxsuzwussurqwxwuuwzxts{yyww{whxݾã쀀bouzx~~~y~z{{y}Խü¹¿ĽȨvvzuj~۩lZbbdggffimlloonkhfdccYeywmx|wzz|qhtljj_\cad```__^^]^^^^^^^^\\\\\\\\\]]]]\]^Z`c_bb\^fchkgqsZ]flqmlvty|qlg_\Z_^X\gh_cib`b`aaZXgnkjghikifhjiinmlkiffhkooifhjhjnkeehhjlhgebgmkpmh]J;52**)((())''(('%$##.9FUcmpuz}}~vd_r{wN8LeH%-B0!IcF'&=Z^UV\WK>46516IWT[^YSY\P?03-2RMGatqZNzcIhipi]bd_aisrqsocalsplknpmigknlkmlkmqe]u|}¸Ļ¾¿Ŀƽuyururnqklllmmnnd][Z[ivutzywytclviuyvvuvvvussvyyyyzxutuvxwxuv{thrܹĤꀀ^st~~}}}|||zԽʻ¼ʽzlիvW]c`afgjijlnmopnligfeeg\v޽Աͻw}yuqnmkkjiggeeeb``bb`]]_`]]aaadiilokd\eqsqiemntxtnnlea[]_``chlcljie_cd\_iloqjffloonhdelmnnjfgjnolgcfijqkghiffkhhjidgllnleWF:40*)(((()*(((('&$#$!)9EUgw}~~{rgl}|bET`<-AF)'K_G($C]\VWW[\M:8997=BBOV][Z_V?.1,3TOI^qoYO|`Kgfjg^df`cjqqpspc_hprnlmpolilnlknmkmud]y}ƸǾȾ¿þtwtqsqmpoonmlkkkd\XWYiy{tzzz|wcktduzuvxxvuvxwutyvtuvvvvswx{xwzqiw޻ƾ退cuq~zz~}||zy~վɲ¿Ⱦ֤bV`b]bffhlkikmllqomkihggo`[ෲƼⷞ~vjjgfmrnikojhknuqxysmold`^_`acfkomlgjjee_XbmlmojhjlmnmhgljknnkggjqqojgegiljjheejnkklidejjlkaOA:3-*)(((()*))))(&$#!1ARar}~{~zg^pjOXX33PR#+SaE!B]][\ZYVC:3582.49:EW\Y`X=-0+3UPK`sqZN{_Lfjqjbeb\dnsrqure`hoqssqnoppmomlonlntc\x|źſþ¿ûſľtusqrpnooonmmlkkc\\[]jvvw{yx|xfm~shy{suzruursxyuttuuuvz~uxwxvv{tlz־瀀h{r}}}}~|zȳǽ͚tUZdc^`aedfeflonnqpnmkjjjmsdw述亘ȴw~}zq]dffdyrcbfgmvysrzwoogfhptpkfcjlkmnkhjkkkkigfhjkkjgfhjkllheilhjiifbeiikl_J@<4,)(('()*+***)(&$#'5Nhy{iUhr_ZM6BXS24W`?$%CQXUZ^RC6-*1:3)*/.6FRX_S8.2,3UPKbuqZNz`Lhntebie`hnopptpc`gnjopmijlmmonmonlotiayǷƿſŻtussrqppkllmnnoog_[ZZivwy{wv{yfn~wkvwtwysrsvvssvwvvwxyyzwxvwtv|unyĴƫ怀dx~~~×ųû˔b]``^ce]^cifdfmmkkqpommlllfunaм½库ɓ|nnpkbƏhhi`ozjonijgknnlmkfenlkjhfgiiiigfehknkkiegklmllhghkklm]HC@5,((''()++***)'%#"" '6PfrTXsuf]OCOQ90CbU2!(C\a\[T?40,+4:3)(*-05CW_O804.3TOI^qoYO|_Ljjjjeha[gqspptnb`iooponnpolmonmonlosg`wƺ½ĻƽƿŻvtutsrsqjklmnpqqd\ZY[jz{vyww|yelwgtzxwtvssvwuuwwvtstvxxswx{xwzrjvؾ˪退d´z{y}~||νÈa[X[^]ed_egigdhnolmqponmmmnipxinͿԿẔŒyyiٜojg^uʸfssjkgkpojhhhjponlgeglmmlieeghjiihcbipmmjfdfhkmlZIHB3,('''(*+,)))(&$! !(:ew~_M_z~e`]HBG?>Zb>'$&?]^\V>,.-/.371/12-/.7Q`S>25.4TNI`spZO{]Iljjfcie_hooqqtpcajqooonnnmkmomlonlnq`Xv}{żÿþſusturrsqmmnnnnnoc]\\]kxwvzwvzxfnvgy}vstrvvrrwywoswxwwvuuxwyww{sdwջʣ뀀hŵı}~|~|}~z|ùùɺɻſӷcZW]bYZbbafgiihilkjlpoonmnnooclsg~̽ٿ㹗Ŕì˚rje`xáºeqrgjkjnomljikooomifimkmlfdehhijigffghlkheehkmliWJLB0+''''(*+-((('%" +Rw~vYZo}o^R:.AY][G'(/+=Y\VG0&.+-+3;:9::@ED@K^X?04.4UPJburZNz^Gjlricgb]forrosre^fonoonkijmlnlknmkmsb[x}ȻĿżÒsprsoproppoonmmmi`\YZixyx{xuywgrwjxyrtzsuwxusstyxwuwxxuwyvwtv|ufyóˣ뀀jȸȺ~ƿ˾ڿYXb]WZW]]_ecaaghgikloponnmnoohlnrflⴖػΙrnh]tƶgruiggoooqqlghnmlljhikrrmeafklknlhggjqmlhgikjikfTJNA,)''''(*,-(('&$!#&BpeV\vvS5(%0EI:2)(%":YbS@7,%&&&''())*'((&!2\zu`byw{p_l`2#($(*$*,'9Z\EIhnhljjllmmmmknoYCV^A,4- 6SQK_trZP~eLjjngaec^fnruqrrf_hopnoomnnmjkmnnnmlsg]tƶüþɿ̿utsssrqprnmopnkkh_[[^kvvx|zvxyhmweqxuuwzwuuvvwwwuuvxwwwvyyzwx|uiyҺɨ怀nŲ˾x}zz|}||}~}yyyx}}}}ͻѺuRac[aic^_\YXX[^`dghikomijsqlmkd`ik\TUOO\Sҿܮ}͚ond`u̗ùgqwokggoomnhcghknnjhillihhegjjnmkggjkknoheggjng[I<3*%*%%&'())*))('#0_~~}d\r}xtyr\bQ0(&(#+&)1.:W]KPnodgeda^ZWTSRLMA8R\=.4."7RPJ`vsXL{_Jkkogaec^fnrvsstg`jqonopnopoppppomkjsg]vǹ“wurrttropkjnommqj`[Y[hvywwvzyfn~uhu{xvttuwxtrtyuvsqt{|xwwtwwy{phzҹ˨瀀mɷđz~{}~~}|zz|y}}zxyzzvvw~ÿзž׳iMcbZdlf\[[ZYYZ\]ceedhlmjiolb[RNR^ZNLRNOVMVܽڰɒ}ƾϛomd`v̗fouljehnlknjcekjijljihllnlgfjlpkgehlkholfffflogXC3.(%($%%'())**(''$ 4h~~kVhzypqzyr[?,2D>/(@IFLED[`G>@A>:AAAWY8/4.$9QOJ`vsXL{]Jhkqhbfd_gosrqrqd\hqrqqqooonmnnooonnrh^vÿ’wurrttrolmmlkmopf\WX\ix}zxtx~wdrudxzururqtwwuuttuvxwwvxwtwxy{qh{׶Ъ怀fŷ̿{~}zy{y{y~{{}~zvtyÜҽզ\Lba[`caWVWXYZ[^`defgilkimh`ZQIN\SEHNMWZMSJk۱ʑžԝnj`^wĕȻdmrigempkimjegknonidejlnlihfgknlighjkkkihidemm\O:..-%!#$%&')***'&'$8luW_qyonywr[?>ACFJLNMMPR[ZV`Y;.4.&:ROJ_trZP~aMgiqicge`hptsrtsd]ispnoommmlkkklmmnnsi^užü¿Ŕutsssrqpnoppmjjle[X[_jwyz|wuzucosbrxvwuutrtvwwwyyyzwvx}wzz{xy}vh}׸Ҫ瀀Y|~}{{~|{|~z}}{{||}}~y{ӽƶ¿ĺ١VL\\^cdf]UVWWXZ_cecdgjjkkgZRSUVWVRHFJW[T\SJX೒ѷ֟oj`^vԿcmqfefkplilhdhrmijmjhkookghhhkikkiddinjghgahrjOJ<00/&##$&')**-)')(" !!# Cq|~|`Xerliv}wlXPc\DXkLOgnl_IT`ZUY\[SORUVTOJFRZ_\X\R=-2-'AHOMKHQbkmpxonihhchqbUü׭ھgu{u}z{Ľԙmhb]xýɾcmskkknpmlmkghjoomifihgkpmdflnjiihghllmkefhijeidL=B:% &+-(%*'&&''%" !>w}{~x\UmyvmWVf`[lM&LqaYi\, <[_[WYVOLPMJKNNJERZ]WY`U=,50$9VSK`tr[P{`Jglsg`ca_iruwqttg^gsrpnmnpomopommpoktmav¹üƿurtpornqqmqrljordZVW\l{|wwy{{ufqufw|urrvuuutvvrtyxtrvwuvxvwuw{tn׷˩ꀀU}~}}|}~~||v|~}}}}~~y}¿ʻ\FW][[gmjh]VW\]Y\bj^H;@FEDPY`juwsprlmlllhji[Wئٽkz}їnic]wɾgorhhfnpmlmkghjllljgijigkkgfijjiihfgjklnjgeet\:=M7#+0,(**+++*'$!!#T~~|}~~x]RjgBKibXpO%NsSShY)>[``^`]UONUOHLMHISW\\\^O5+51%;VSJ_sq[P|^Jektidga]hpqtrtpd`ktmopononmmpqnlnomqiaxøƼſqpvttsmlpkjopmmpk^YZ^jwzwxz|zsequfv|vvxvuussvxvxttxzwuvyxuwwy|sl|ӻϩꀀY}{{|}zx}{wy{Ǽưb`YYY\ZMGOXUXaimhabXW`^XZaaOHNQSUU\hpqrrpqsrttqpon`TZئnz~{Иpld]wŹepvnnmjooljfeiljimjcfjllomdbirlkjjghkknnjgaewmH14:5&'01*(*)'&&&#0h}}|~~{{bOhuL(Ko^Ig`A\hBRt`.=W[\\]\VPCNMGHIIMU\`_aaSA-60$9USK]qp[P}aKhmugaeb]hqtsvyp`_jpnprqonnonnoqnklqngayǿ̾ļÕppuutslloqpllnrvk^Z]ajuxy{vv|t`nwdtzwvwzwvwuuvxuwxxuvy~zyuwx{}shxԾϨ耀T}}||yz|yv{}z}}~¿´ý\[[WWTROHKXXQUaili`b\\]Z\aaTLIJOYcdinnmqsrqttwyurqj_Q`ԯƊs֜qja\{ó`nvkiijooljfeikmoqj`bhmmomheilkjjjhjmnkjhgbar]1$%)@?##,.+(*)&$$&%!!Q~||{}cG_}_2#Tr\8NcZ^M6E\P, <^^][Z[XRTXZVRONONTNEEB9418/ 6STM`sr[P{bKjnsh`dd_fpvtqute]gtvqnpqonpnmopokmqskbw¸ýǼƾľǿŔvrsonqorospihknph\X[_iuxyztu}wbowjvtms|vstutrttwvusvyzyzyuwx{}sj{Ъ瀀`~~zz~}{{{yy~ƸĸĻd\g`]]WUXVTXWQR\ellg_Z\]VVWSKJDH[jjgfknptsnkonpwvwvh[H]ɼֲo{}~ľ֝slb]{Ŷdsvhegnpmlmkghjoonidgiflqogcejljjjghkljkhhd_fqS$IP*"""&))++-+'')(%7m}{|~d@OfD#(CNA-1=@A0/.14-+@VSPLHIIC>=:6433311-+*'&)39/4SSNdwt\OybI}ilqjafe^cmusloti_ixtoorrmlolopmmopnvk`r}ĵĿ¿ÿɿȖurspnqnqrpnmmooni]XY]jwzyvux{whrscu{wuvtuvvvxyvtuuwvuuuyxuwwy|sl}©Үꀀ_~}}~~}}~}}~{}}~ŸĿçq\ee[Z[WV\]Z\YYW[eigcd[ZXNIJF9AJXgkjkiorturqvxtqtsvzlSB_Ǹܮٺs}|}қtmc\zƹŹakpfilnpmlmkghjmjhgfiimnmkihjmnlkiffiilkfdfd_R2! U[2$%!#&(+*,+(()& N{{|~hEIS;+30/37328;@FEHNQSO610,(+,%(5628=?D@-.8.%(*290 5SSMcvt\Oy`Ihlre`fd^fqxvqsre`jupopspllpmnnlnqqmrg_t|Ĵÿʖqpusssmmokmmghorg]XY^n|}ywwyzugsvhx|ustuuuuvxxtwxuqtyxqvxvwuw{tj}®ү쀀a|}~}~}~ǿǿhXbcZ[^[VUTU\QXY]imgdc]WLCMWQAK^ihhkkgjkorqs{qrswrqteOEmȷڰؼt~}ԛqi^Yzɾfnskkhjooljfeihkjjeadfkllmljjmmkkjggjjikhb`mkO(6hW,$#!$(()--+*-.&8mzz}}|dILSKKOLOUYTYaY\TWc]_cJ+(((%*,%8[fahhdmU./?,((080"7TRK^rq[P|`Kgmvfcic\fptvsvrc^jwtpmmnnprplknqonopfd}ŹýɿƑrqusrrmnqlnoikojg_[Z]kvtz|vuzsaothwxpsxwttxyzzywwwvvwxxuxwxtuztj~İϭ뀀_~~~~~~~|||~{Żǽºq]]`^ZXY\\XVXZTT\fljdaWLA=DKHMallnponklkjmrtswttttwtiIHܱ۵v}қqjbZyżempjlfnopnjgffijllifhkklolfjmijmmgegklmnedcmoJ$ A`>&($#'('+,..-*&# !Yxy~sYOPPNEOR[YZ`[Z[ZY[X]_K*'+%")'&KjogfjroC0A@').*06-6TQKaws[P{bLhoriagd^gqvrsutfanvspllorrrrponoonlte^w÷¿ſľĘqturqrpmnnknolopg^XY^juyzxwwvsiwwftyxywyxursutqtuuwyutyvwvxwx|sozƫи뀀d|z{~~ſoX\ba][[]]ZYZ^UNXikfg`RKMMMSYhkllonifhklklprrsovvox{hIUگ޻t֛nid]yúʐgoskjfmnonjhggklmmifgjnpnhddhkimmiehjknjcc^joJ#!"*;+  !"&(*--..+*)$ Hzruw{y_ORUTQJ?IXXXZ\[[Y\X]_L+(*'(+)0TpjkmhnbEGVB"%,*06,5SPKaws[P{bKhoqiagd^gqvxwvsd^lunnnpqqpopnmnpqpnsf`xĺĿŔquurqrpmllimnkopf^YY_lx|r{yvzwgtuewzuvxpquwurswsvwtrvyuvwvxwx{rh{Ĩζ退g~~ÿ¿vUXc`_]\\[ZZ[Y[U[noc]UNMQSWaijijjjlmkdimkkmpppuysqxtcRVֵثܽv~|әmie_|ûƋcmtiihkmnmkihiijlkhegjkmmjfdhmilmifgjkmkif_mnD # $%(-.,-,)+,(  3r{stknrzmWIT][^XCBX]WZ^ZZZ\X\_L-*)'+)&9awhjmhmVJGH7$%*1/5,5RPJaws[P{aKhoqiagd^gqvvtspb[jsqqqpnmllnmmnpqqprfbyƷý¾þýŔqturqrpmkjhlnkopd]Z\bny{u~}y{ucqwgrtuzyqsvvwwwvvsrrqsxzwxvxwx{rhŮ͵耀ez|xv{|}}}}}}|{{}{|~ǾƿdU^`_][ZYXYZTZX[ee\YNKINT]ejejpoikkdbimmjlmnmvvrtvj[YSgغת׹uΗojd]~øamshhjjlmmkiijhiklighlomolcdkllmkhdfjnjoohdl_1"##*""&#&--+,+++-)%! &_|rm`fmgOBKURW]N?N^YYZY[[]W[_M/*&&,#"@cqgimkiSF=2,,)(1/5+4RPJaxs[P{aKgnqiagd^gqvrqrrc\irusqommllonmnpqporgc{ǵĿȽ¿ÿŗquurqrpmllinnlope]YZ`mx|wwxzwqevwhuxwyvqssruzysuwxxyxvywxvxwxzqjȭ͵耀e|wzwyy{|utxxyyxux{{yz|zvx}{}|{ÿ¼úutwWWb^][YXYZZXV[`ZKGLH@BTdfgkggjklpofbinlkkmnmipvtqhTUXPgԴ٫׷t~њqkd\|þhmtjhgjlnmkiijjkmmjhjmllnkehmjnmjfcdkpjmjgf]C!"! (3)#%!%+-.---,(&&%##%%!"P}i`Xgpj\SILWXZZSEP]ZYYY[\]WZ_N-'$'-$(Nh[TRPMHH@C4*/+%'05,5SPJaws[Q{aJgnpiagd^gqvwtvug]jssqooqrqoqpnnppnmrgd{Ǽþúļ¿ŗqturqrplpoloolopi_WV[iw||yy{ztj|qcy~tsuruvuuvvtxzxwwvuyxxwxwwzqf|Ʋζ退iyy|{}}||~~zx{~~{zz{~z}¿ƿvaZfg[U[\^]ZZ\\[XZ_^PCAC<>I^ifbgiddiklkifkmlkmnnnjlsqgZPPSOPqұڬܺv||{ڟqke]xÒfismigkmnmkihiklmlhefiejlhhjkioligddjqrkeieJ-"%"""$ "#"&+.+)*.-**-('##-5/ F~hXW_\T]^RP[acd^[`]USQX[]^VY_O,#%*.)5][><@:.&3HF.$+'#%17-6TQKaws[P{`Jgmpiagd^gqvxttte]kwvrooprrprpoooonlshc{ǸƼÿƓquurqrpmrqmpplooe]YZ_juwuyutxsctsbv{sruwyyusuvtsruwttutxywyvwypf{ζ退m}}~}}ſ~rsuj[a[OQWWSY[\[[]]ZX]WIDHKIJYcfigee`bflnjghjlmkloqpookkj[NQOJVWQٸ٬t}{۟rke]x¶agtlhimnomkhggkklkgcdgkmlgdehklkjjgehntigl_=%()$'(! !#'*,/1.-.-)''''#"%!'26#:sfV]WBO\RIT\clhcb]WQJIW[^^VX_O.#(,*);`hZ`f\J5*IK3''$')28. 7USMaws[P{`Jfmpiagd^gqvwrrrd\jvwtpnoopoqonnppomugbzƹŽƓqturqrpmpploolopc\Z]bluwtvsszudsxhwzuutvvtqrvxvsvvsqvzwyywxwwypj}͵耀k¨}{~ÿÿĿvk`Y\^YQZa[PPUUTXZYZ\[X[]M=@DBBWde_biiediihlmjkmnlklqrqnjkj^TQSQNRUHZĶثŠu{}}~ԝsld\{ɺ¼enwidknopnjgfflmnmifgklknlddkmiikmjfgllejkU5$'("##!" ##%,.-0/-+,--*(()'!"#&#!$6,.d|^Sg]>IP>Ea^Wa_aZNTZOQW[^^VX^O2%*,$#8Y[bgkjk`A:\U9'&.(39/!8VSMaws[P{`Ifmoiagd^gqvzuvwf[grpqrrqonnommnpqqovg`yźþÿºŖqturqrpmnnknoloph^XX^lz}vsy{sdq}kyztw}wttwwtuyxwvwwsrwyzwyvwypi˳怀lȭ{Ǻƾȵpula^^XX_a\[ZYWVTT\WW[\[\\WWIBNTRVakkeeedddfikkjjjfkokhnpijmj^SPSUUWRV[Seرw}{yјokcYybowgdhlikpnggmlhlnihjgnlkjkkllkjlkefjkhiplN."%$! &&!%'%##'+--.1-,.,)*.(*(""(*)& #% *.*^u^MPK'!"!! %)'')'%$'*,.0-//-++*))*)%#'+,)&  '& '/+\t]A766@YYXa[TWda`^ZVPINONLFMWJ0.2*%.-)-)&?fpjo\:'(''++09/5VSJ]wu]Pz`Lhopgcgc^dmvyrquiYdurqppqponrrqpooppsic|Ĺž½ÿÿļǕossqprpmnpllkkqsc[XZ`jstwxxx{ucw|hu{tpruwvvxuqtswutwvsrxwtwwy{qi»退rįâurfTYZZ]^\\[ZYWVTSRWUXXTWZUNAN_^Y^aSIS]VOUY\^W[`P.$',5@7+(+#.SlljhD&&-'$.09/5VSJ`xt]Q{]Okrmfdib]enuvxvsg]huvsppponnmnoppomlqi`w»ż»ďqttrqsrnlnlnnmqpg`\\_kywxyz|tbv}ivywvtzsqrrtvsvssxzxwxvxvxtv{tl{倀tί˽̵~|zjWS[ZW]_\\]]\ZYWVUVVZZXXRD?8=P[]`e_ababdfecehiiiiinmkjnrfOMQTTTUUSVOSWQUZUSԯ׶nyzy|{̕opg[~ķÌckshgillnnlhhjoljiffilmhfgjhhijikiddjkfyZ2',+%%()**&)))+,+,.-/.,*)))((),-.--$&#%/03,&FZQ=,%'8SYQQSOCT]UMXZPIRUYZTX_O-+..Hi\8+.&/TlliiJ-(,'%.09/5VSJaxt\R{_Mipoeeia\fouytvxg[hvqrrrsutrlmopponmpjbxſƿý»Ďqturrsrnnolnnmpnc^\]`itxqz~xwrbuyizysvtquvvvsqvwuxwrtxvtwxzwvzrk~瀀sδǿƽʸ~x^RW[ZY[]\^\[ZYWUTSUVTX[NAC;;JYZZ`ca]^cbacffggfffghgglpi_VNNRTSTVTQHGRXPNSUHnӲڿw~ЖopfY~˷cjrigilmnnkihilkkljihjkkhhhjjiijnoihjjjwsS8*#) */*+*))+,,*+-+,./.*()))*--++,'&**+,6*5?6,'&(6R\P?=FIJOMQZUIHNONMHQ]Q1)*6ZlE/0"/[ngkjF)&*(),09/5VSJcys\R|aKhnpeeja[gotwuwwe[juqrpnorrooopqqqqppjcz¿Ŀ½Əpstrqsqnrqkkllpoc\YZ_jvyzyy|vbvvfxwsxyxsoptvvvvrpruwwvuvuyyzzor倀tѷʼȷtynVNXUX[[[]]\[ZYWUTTYYYZUG@C=KY[Y\`_b[]ba`cecddddfhjcdlkWFHRSRPNOQSSMPXXLBBEEWѲٽwԙqncW¹ŏdiphijlnomkhhhjlnomighlljijjkkiilkffiikkeQ8*$&K\6%,-+))''+0,*-20*(,**+-*''*&$+)(+2*$($&)$ :OOA;@PZMKJRZTKLGGDC?JYO5!":bzeE)&$=epjpb8"),()*09/5VSJdys[R|aKhopdfj`[gpttxytd]jtutsrrqnlppoopqrrskc|¾ȑorspprpmqpjjlmqpj`YY_kwz|tsyu^r{gu{utwyuvvrsxxsstxxsquwwtwxy{phu wЯ~~ɿȤkxwdWSUPV][Z]\^^\[YXVVSRZU@=A8,#2UijiiV+1/%&)09/5VSJdys[R|`Lhopdfj`[gpttxwtgZewwsoonmmoonmlmnprvja|ƿĽþƿ¿ʓorrpoqplmmjmoorod[WZboxytz{xzuaqou~xsxyupqwxvwxxwvuttuvxvwtu{ui䀀uԳ׽ǼŘwiyqYXZXYZYWWY[Z[[YZZVQW[XF8=HJHS\^[[]^^bb`aeebfeddegfehjWCDGFKKJKNPQPNKYZQKC3/*))()*&+..--..,1/0+$(+#%)&"&-+' "DJFEEMTA4;@>>GKDEGBJZQGUTMMWWY`V1(*4QkT-1:Xnigjb?+&-+'*,9<0!9XUMdxs]Rw]Ljoqgcib[frwqswtb[hptqopqonnlpqlilppxi`~žļȖrqrsspoovkgnrnnse\[VXn{zuyxtyu_sqr{xustssuwwtrtuvuuvvvryxwsw}qb¿Ļø怀sںڽҴqroVRXYXXY[[YWZ\]\[XTQWZSFCGNUYXVVVWYZZ]^\]`a^``acb`djkT=9BGFFLOSSPNNORUPKJC78605=1-6?AGPE?FX^PJQYQOXZ[\H/+/*.HbrmlmmiZB-.0/,)&&+58,6URKdxt]Rw`Okqsgcib[frwywwtd[gnmqsommmkjnqpoonltkbz¿Ŀ·þotuppsrmopokjlnmd[[X\oyxzytqzy`qojxsqyusstvwwvutstwxwuwutzxuysd耀tظÿÿ~dNUXVXZ[YWVWVWY[ZXY[YOGDDMVYXWWYZZ[]\]^^_`aa`bbbfi_Q02>C=CLJFGJNRQNJQMGCAAFNNNiǾЩٽ}әmjfYwȚǍ`hpgfihlllkggnjlnmhdfjmjjkjfefikkieehlmgi_mx2"(>jP%#+)**%%(''*-,,-//.//("! )-+)+)$''#!'86;7/IBFMIHKHIDP_[QLHMJLVX[]K/(.,'/9FV\ZSG4'(-.)&)((/48,6URLdxs^Qx`Ojosgcib[frwywwtc[jtwsqtqmnsmnppoonmrldx¾¿ĿÏnuvootsllljikppmd\ZY^owwwyvt|z_plo|wtxstuwxwspruuttvwvtvwzvsztgꀀuӸ}~þκ~w~~ZMWXXYZXWVWXUUVUWWZ[YKA@FQYWXZZXVX[][[[\^]^_`_^ac\J918@>=BHGIMQRPMLLOKF@AIOPNJVŸ̦ڽ}ӚmjfYwàʐelthgihkllkfgnmmmmifgjmkjlkhggijkhedhlmkk`nc+!'/UM/&,(('&&($'+-,+,--/0.(!"$&)*+(&%(($$IFY^D=NTJKX^XVSGHKQXVZ_P1%*+('(/,75-.-*,,,))-'$-58-8WTNdxt]Rw`Mgmqgcib[frwvwzxeZhtvspnnnnnqonnnoopqmdw¿Ɠnuvootslkiikmnnoe\ZX`ouwuyyv|w_rjt~xwtvusrsssspwyvuy||wwsuw{}ma怀sٻ˶üҲ|fonVTWZ[YVVWXWVRTRQSXXUJ@59JVVUVXXUTWZY\[[^_^^ac^^bZG848<<>BEFJIJLNNMLLQIB?DPRIENMjȥۻ|ԚmjfYvźʐdlshfgikjmojehnlmnlgfhjnokggiijjjhefhkpljmtg@!''&+47/()%$&&))&#)-,*+.0-.,'! "$&),+)''"!&7?4;=+!/GRR\Q58KLIOZ[V\]RPUZ]VZaR/#(-**)/),)(,*&*))*,+&'/48,8XUNdxs^Qx`M~flpgcib[frwtuzyeZjxutrppqolsolmoopqqlcw¾ƽŽēotuppsrmlklolhjpg]XW`otxw{ytyv`uiuzuvrrtuwwvtswxusuxvptyvuuz}nd߀yոDzƠ~sd`UWTWZXWWXXVTUXUPRUOD>86ATZY[TUVVVWYZ][[_`^^ada^WG849368@CEKLLKJJMOSEDIGECDC?AFKJOQMMD204)5URLdxs^QwcPgnrgcib[frwtsxzi\hsxusrolnsqmmrtqnnrjayſƾrqrsspoojijnonnqk^VT`nszzzvt|zarkpyqqwoqrtuvvvuttwyxy{yuqxyxzqb½؀tּ|¿˪ynXVYXWXWXYWSRTURRTPE>>>=FU]YVXYSSYZVW^YY\`a^^adZF1,47457:>ADEEJIJMMKLQOFCBBDD?HHGLLZŠ}ٽyؚlkfVrɨ̌akpghfglmlmigiqmjkkeelnljigefhnnheihgjpkdtzU>IC,&(&(+-)*'%'$"%&)+,++-.1+&#! ## !)+)+,)##/DW^^]\\[ZZ]^`^\Z\]Y\__][\^__]\\\\]KKKJJJJJHGFEDCBB??=:62.,,/'5XXNcws]Qw`N}ipqichaZfsxnwzsa[ittrrqpqqnlnprrqomxqf|ýſÿÿÐousoqqmlnnmklnood[[[`muyy{yv{u^tpnyursvpqvwuuwwyvruwvvxvu{yuxpdIJրnβ}~~}üʡ||}hVQX[VZWVWWTSTSSWXL<6;DLUXXXVUWYWSTYZY]\]^][\`RB30368;569<@CFGIHHJIFHKFCDB<>DEJJEDNNgľ}ڽ{؛mjeUtаΎbjphiginonoliknmllkgeilmmjfefhmnifhggkihimqW8;D7 &&)#&)(()&!"'),-,-./.+&! #&'#%&+/+)/0*"+CX]]\\\\\\^\Z[\]ZXRTUTOLKLGFEDDDDE;;:87543....----.-+*)+,-11) 7UTLdxt^RxaM~jorhchaYfsxxvuse\jwtomnoqrpmoqrrqnmrkay½Ľ‘ousoqqmlmnopppoofZXYapuvtwut}{`olkwutvsvywqqsuuwxvsstuwutzxuwod׸рl̰}y|}~{ȿt}|}m[NT_`\WTUVTTTTSXWI:BGIKKMNMIHIG@@ABGKHPQOESOKû~ڼ{žכnjcTw˪БciphjjinonokiklmmkkhgiimnjfefglmkggfhmlkkcZchYC:'"%&+'%)(')&""$&()*+,-+)$#)*)').2-,3/' #:NRQPNMKJIB@>>@@?<9::840/./.-,++,,++++++++++,,----000000//11+#:XVNdyt^SxbL~jnshchaYesxyutwj^hvyplmoopoopqrqomlpi`xſľ¿ÿĽƿĿ’ousoqqmlnlkjjknpfZWX`ouvtwtpyy`opo{wtvqsvvvwvqmttsvtt|uusywuwndʰʀpӸ~¿ǹp{}{xfXT[^\^WRSUUTTQMOPC9@MSTVYYWWXWTU[[VUYZ\YV\bYI886203675558BGIHNRRRSROQJKQUYXPQNLMUQIRPYĹǺuϿžͷy؛mjeUt͘͟gfrlgfinonokikknnllifgkkkigfgimiihfhlljsdaqaOBDRT>*"))%')%"&%%'))('#"%*)$%)'(,-.5:76431,#!%&(''&&%%$%#!!##!$"""%$! !"  "$%$#"$$$'@XSLdxs]Rw`M}jorgag_Xdqv|wvwhZgyxrppnnnkllmnprstpi_wĽ½ousoqqmlkgimniimh[WV_nvxytpr{u]tkiwuuxtpqtwxuptvwwwxwtpvvvvz}naҼu´¿ukvrs~}z\LW^ZVUVWUQQSIA?>;;8569:84889;?CFHMPPLLPTTSPQQORXWQOXORYMSRRi̽zʾît~~~ؚlkfVr͠˘ggslfeglmlmigilpnjiiggjmmieehjnhhhfimklkebgoqdEEYPIQ;*$*("$('$%$$%''&$$'+,&!#()$$(+397740/.*""$"!!%%%%%%%%++,-/01155568:<>E<9>R]TQfzv`TzaL~jnsfaf_WdqvxxwscZhuysonnrrnrrponnnnvodz¿ÿĿ¿¿Ĕousoqqmlpkjnnklrg\ZX^lv|tvvuyrYnop}yuutrsusrtutwxvvttxqwwwv{~obϹt׿οĿpnwpt~}p|nSNZ`YPOSSQQPB<=ACLVWUUVVVWWWWWSNRYXQ[WC5<=8;<=74:;8:<56<=@EDLNQQRSTTYTQQRUXWQOQURNLILQVٳyņr}}~חnodVzͥɕbhthhfhkmmkihhllkklhefjjjiiijklnkebejlijhg_fviG^jL?H6$$$%&((&$#"""%((%',-+(##&"%),-035355/-/&!&'% " !#$$%%%%&(*+.10--279;989:;=@BA9>IMQVWUUUVVVVWUUSRRUXYXF309??>@948;859957;:=DGINQRPPRUSTWWRRVVPRTSMJGBOROjйzÐyۚmkaU{Ѭ̘cishgehkmmkihhmnnmjfgllnomjijmlnmieeghmniifgme`th:(31%%$$%''&%#$$$"#%'*.,'%$%'%$$)/3216310.-&&+-*()++*-36533369<@@@@ACEFGIIEEINPNNMMNRUVZZTW\^b`bb_]_decfgjfd^RTexv`RydP{knqhagc[epxuuxyh^jtspqpnoqoqrqpppposg_|¿ƿ¾ûŋkttqrqmmikoojgjpf]XZ`mwxswwtzu]qgmwtvxvussuywtvtutsuxvtxwxttypeƳsռ¾ÿlkmfrrn{sZU[YWXVROKB?;CQUTUVTTTTUUVVSVVQOSY\H3-6:=?9<4389556646:9KTTQPQSSSTTUUUUUXUPSVJ7&'-36:==87;91054335;@ABDHFGKNLLNONRVVWVSQUUI?CLNUaLEʼvzٝmidVxͥΘchtijjhkmmkihhhklkifeilljhfeggmklljfhllljgd`jn1$&&#%&$""$%&&%# !$%$'&#!$%&('$$&+./.0242140&"5N[]\a_ab`[[_Z^ca][^baa```abcege`^adecge]]ce`cc]\]^gh`hi`\`eeegf^^^RNgusaSubP~imthagc[dqwuvzwdZivqqooqmkopnnoomlnti`xſĿ¾¾ktuqrqnnjhiklllmd[VW^kuvtrsuyn[yhovquwwuuttstvrutrtwvvvuqtuvxkjٲrƺhjz|zxxtxqufWWUSLB@?;;MWUTTSTTTTUUVVVURSXXL6&'-07ENKGC?>>4-/6666;CCBEHDDJMLJLNNQSRTWVUYUG;BMQOD9]аs۾z֜lieVvϦ͗bgrikkhkmmkihhooljheeihkmlgffhkklkiikomlgjiZ\tQ%#*%%"&%#"#$%%%##" #$#$$""%%%''&%'),./3/2752/+*#)BZa]Y\\^a`\]aZ_cd`^^_bba``abcdfd_]adefhe_]ab_bb\^_ahhajkb^eihhig^_`SNeus`RvbO|imrhagc[epxtuyyf[hssnpspmnnqnmppllovk_vǿý¿þľ‰jstpqplmkjjllllmd[VX^kuwvttvzoZvmpytqstvwxvuttttvvuwywswvwssxocԫsþƼnrxmltoqor}~qcXROI?:=>FRVTVWUWUUUUVVWWTQSWO<*#$0:DSYRKHC<:6..6;;9;ABDJGCDJMIIMMMPQOQVWQXUC8>GF2-Kѥ}mο׹{њljhVr̦Ϙbeqgiihkmmkihhllklnkggjlnlhfgjnonjghkmnmghhc]N."'("!!#"#$%%$#!#$$#"!"%%%&&%%&()(').25037511//+4L^`\[[[^a`\\__`bba```_^]]]]^_bdb^\`decca``ceffe_bfgkidlmc`hkhghf\^aUOdvu^PwbNyilohagc[dqwuuxwf\hquopspmnopoooonnnqibyƽĽ¿žþĻȽďirropokknnmlnpold[VX_kuwyxvw|qXploytrtvvustutqusuwuvwvtxwxttypaӲwּľønilkotvqqtrlqyy]SMH?=DJUXTQVWUYUUVVVWWWSUVO@0('(8GMMJC>;;>@EOFCELLGHNNNPPOQTUY`YA2;C?;RѡoӴ{ԜjddTsΫЙbeoegfhkmmkihhkmmlkhgkpnlifgikimnkikmlknhcmC%$#$#$#""#%&&$"""#%$!$(($$&&%*(&')-./64531340//;Q_`_caacea\Z]db`_`aaaeeccbcddbdc_^bfgifedddfhfd^afgidgoncailgjkg\^aUNcwu]OxbMwjkmhagc[dqxxvwue\irrpmoroovoqqnnppmsjawľĽĻſhqqnonjjipqkinoke\WX_lvwwvvw~uZpkqztttstsqrvvtqvurtutsxwtwwx{ndĸqƿž¾ùm_ksnmsqhouki}yVKQG@JT]RVUTWQRTXXRRXYVX]W@65-*+CLG;,*,;B>30246=?88DEEPIBEMLIJJJLNONNQTWVYC/9B?h¢|nĿ˾ѽzՙljeWy˯ǘaipifigllklifhmllkhfgjlpnihddhlnomihkolon_|>$#$$ # !""##$$$" !"" &')('&&')&&)++.32454477657;F`bUbdabdb]\_defedcccadgea`ejcikfbcgigikd]ekelg`]^bfheig_\__ZY_[]b[RMg{u\QvcSzijsk`b`]hqtstvwh[gutpqqooqqppqqqponvoe|ƽ½¿½Ìkqtqoonkjkllmmll^YZV[lysutxtYtgmvssstvspruuuuuwsosxwwwutou}mfĀwüýɻjbqxmgmoktxpv~v_JFNTV_USOQSOUVVTSVXVR^YL835-,7DBB@1''29;88975;A@:;DKMHBEMMKLMNNLHINQQQV^M8145,+AE>BL?0./29>??;65<<9;CE>322355313003750+,3?P]\TScvr\RvePvkoug`fb[epwwwyyj]huokmpnlmnpooonmllti]vľ»½Ŀ¿¾½ýǿkqtqoonkiijjjjkkfXVX_kt{wvsszpZxhnywz}yttusqrrpuuqruvvvvvupu}ldнrżѼ¿ɽt`\^[^hqpqtw}}x||fHCT^ZXWUSTTRUQUXWTSUWM>4286)*>EAAEMPN79FLW`WOfws^Ss`Kxmpqd`hcYbq{|xvui^iuxposspppqponoprspi`y¾½ĻþŠkqtqoonklkkjkkmmcWTT_pwvz{wvzpZyfoyrsvouvsqoqwvustyvsutvvwrv|ke€wƽ»p[[`_^hojdgilu|xuzybQVXf^RRVXQTWTPSXSI6/034-'3HJ:6IYVH/47BJIGGJJIJJJKMMLJJLUYWRSSG^ğ~}xnƽƾ⺰͵wþ֚ljcTuǿ͑]nqhhggllklhehkmmifghiiikkhgggfikjfgkooogijcrT<8*!"&##""!! #!!##" "$%%%#&&%'.1146656897960!2iyW5)(%"$&')))(()-../03576?BGFAAGHDCHIGIEGJKLNNLQSVVOPTIX}{tiŷ¿̳xՙljeVxƲː`qodhjimmlkgdfjlljgfghlmkgdcejlmmkhgjmlmihdaxwF51$ "$#"""! "#" #$$"!!"###!"%&$(-1146656897:903kzZ<4;=86KDA?>@FHECEBD@?EFEGCJNMLNOMPRW[OLSIXڿ|xqfͽȷȾʹ|ĽՙljeWyԼЏ\oqhiggllklifhijllifeghkjhjgeglkjighikjmki`c}c9.-"#$! !!""#!%&$!!"#"!!"$$#"%"#'*+/3467569977>41kjYW[\]^_```aadbaacdeedbbeedeicdabigcefdcdeecacabhmnkgG9JSWZPSfvscXu_MempgagbZdpwwtssf\huxmjopmoqrqpooopprh\uþĽƽkqtqoonkkjihhjlmdWVY_jpuxytptjX{cq}tswpqqqrppswrqstvxusuwysw{in¾рsû~}}ýlb[\gggie`fonpq{wx}xzjUPQSVRQXT:)1565/)')+CHB8@OK:2+$!5F??DABDECDGBA@ADFFEJHGGJMOONRWYSPQLHȞwxteŵÿùηz֘lkdSyϾ͉boqklbelnljedikmnnlgdgmiffhhijkmkikijpkmhk_eY0+%# ! !"#$%%$#""!"###!!##!!#&%%())-39328:648<>13hs`a`_abbbccbj`dccjigfecbeghidedcdbdkcmjeeegafejonkhcKEMPY_TXewsaWxdPxknoe`gd\epvxtqoe]gpupnmloqommmnooppjh`vǿÿ¿¼ļĿhnqonnnkmihiiklhcXUW`ouwxwprym\|lqxutvuqnrwuqtrrssrrrswwtvtrukgՀyĻ{èûze]`irihmiacktwxvst{gSRUPKV[E0-0354/(%(,AIA6?PM?4)%&087>C@>>>?ACBA@ACEDDFFGJMNNMSORWRQRJM~Ǜzzwm˷ĿŽȻ{ĽɾזjmfQvҏbkpijeelnljfdhknmljhefkhjnkeeljjihgfglnjee_d_/&&!# %"""! ##!!!""#%" !! "%&%&)*+/4778899889=15k~mdadfffinmjc`bckoeeiccijhghaegfcadibiffecebeckqlhe`FBKMV_TUewsaWxcPyjnpf_c_Wanvzywtf]htupnooppnoonnnnnnmj`t¿¾þ¹Ľhnqonnnkliikilnl^WY[amtwtzutyo\vnuxrpsrrpquvroqturswwttwtustxkf؀u¯yçºkaiqnebjka^hhiloqpvxuZRSNOWM5-1.033.'#'-;HE7;JOJ9)*00.2>@AA??@A@A@@ABCCBFEEFJMOPSOSTNPRJPܺ}vnƿɼծ||´Ⱦ¾νؕhngQu֕bfohhiflmlkfdhjmmijhfekjlmkgfgkiihedgjlijgefrqU,$% # "!! !""'" ! #'&%'*+,05679999988<.7o|tk[Y`ec`^][ECXlmc]hvh^]]XZaaVPWej^OQIDQQDCE@D\omhf^GCLKT_UQevs`VwbP{hnrhaf`Ycpxwxzyl`ispnqrnjmopponnmmlqk`sĿ¿¾žżhnqonnnkjikljlooaXWW]luzqwsrwn_~jtyqqxrstrpstqwvutuutqqwusqw{kbЀnŵ|ʹtgovlkfdjlij`Z_knqwzvn|pYWSYJ3.53-.01.&"'/7DG;6FRQ?--83.16>ABA@CB?@@@AAAA@ECBDHMPQPPRQONJJ{ʚ{tpųġtuüזilgSu՘bdrifkglmkkgdgjlkjjiedkkjhggggnklmggkmiikffii^D-*' !!! !""##!  !!"%"!#"!"%%$&)++.36449;8689;*;tkotWDBAA>;7DSO=,-:==?6;==:,'% """"""!!!!!  !"##!#&&#"#$$%()),07348=;86:9%Ax~kmyeN?4672.0+@^T99=7MU?0=81ES93LipW6-LPD85AJQP?79:;;>>==?ABA@@@A@ADHLMMQPPRTFXŚ~xɺƬy\UVV``Z`Ǯx~zʻӚlhcXwɼΔbhvkehhkkjliefijjllgdfjjjjgfgjkijjggjnjgdfgfmeL1"&'%&'#"""""""  "#$"!$(($!"$$&))(*-4899;@<695"N~w|{uz|zkYL=007,@LDHL9/BSB3>93HM34UljS7.6IWE16:1Kgohfd`G4?NV]UVcuq_UvbP{hmribf`Wanvtwxvg\gssrroijoplmmnnoppnh_y¾ȿhnqonnnkkikkghkj`WVV\ktytxsuyl[|huwrsroqrqpsvtttrrsvtqqwusqw{ki€u}ɪɾvcf]bf_jzwiuyyzspxxffud9,83-220*(,,)+13:BEB>CLRKCHOUVD78:=?=<<<>ABB@AACA@AEIMNPPSTUGXѤ}r˿½ƳTObos~eaд~ƹԘjieWu͸єbhtidehkjjlieejlkjiffhkljghjidjjihgfhmmkfdgclmZ?+-0--+))(''&&%! !#$%#"$''#"#&&(*)'(,09=::==850")]sz}~u|uaL=79=;>RM2/CJ@563:RN05\oeM4<>KVC01/A_qjdehiH4@MU]URcuq_UvcPzjnpldhbZcpwuwxudYfssqokjnoinnnnnnooog^wýüühnqonnnkliijikmj]UXZ`lruttnqyn`|exzrstlqssrrrsrtvvspswtwtustxkj÷Àq°{{ϯȻrhall^k{pgpz~{vpmz{gbczqE6;1-3/1)&,.+,1::6?NH=ANMCFMUYM:88;<::<;>ABBAAB@@ADGJKJPNQRZNBbԣ~nλ¿ſíǓ[^m|j[pԲy}֖hjgVsӵԔchqgcbikjjljeejnlgffhjkjiigeefjkiggeelkojbgejoudQKKKJB642/+(&$!!!!"$%&%##%%##%(()+*((+.5==968<1,"1hyz|xsz|z{|o]C>62?B44@>=;55G`H-8`jYC2:Q`U:5=29:;:<@BA@A>@ABCFKOOQKOPEBHlΛ|rȭśjqxytSuî~u~ӓeheVwնԚdgofigglmlmigihlmlkhefkkjhgffglhfijhgjkgfggehntrlffijh[NF?:5-.,+%#!)$%&&&&%$#%()(*..,'-5:9534, "Ktvpz~y|you|yocN:48;9;=8>G<DHMB57AJK49P84UnjbfhdK=FMU]QMbvq]WxdQyhnshae`Xbpxpovte^fmnqpllppmnnnnnnnnpjbyĿ¿ſiqplmljkkjihijklbUOVbmttsvqtxk]{fusorwusrssrqpu|vouvqqsrptutuinr~{ͭĶzlbdktywtvzyvwwsw{pernW`p;)-*+$10.,,-/15:;:<60+"(Tw}wlo~{spmsyuy|q]G:56<@;9=::G>457=E?;6:AD5E^B3MdhccggK>FMU]RNbwr`YxaOzimph`e`Xcpxxvyue_hppnmmnmmnnnnnnnnnpi`x¹¿úĻĿĽiqplmljkiijkkkkjcYUWZcovrtqv|n\|{fvvrqsrruxuooutoqussusssquuuvia½z}zÿ˶·hgtxtr{wxz~znr|udmiTby<*,/4122/,,/28:<;99COSOHJNOVWA78:988=9>@??@AA?>?BGJJIQRRRJFNPNХ{rʾĽĻ}zu~w~|Wg~ՕgheUvȒahrgidfkljkhehjlkjjhhkhijigefhjmmhfhjgmliffiklnmjginqoc]^[WN=7W\D-%%&&'''%$$%*-+*,/00--4=>6-)#0^zzuntxjjlnqpsx{t_F6D=6468FLU]RObxtaZweQygotg`e`Ycpwutyvg`ipnoolkmnmnnnnnnnnph]w½üiqplmljkmkihijmn`YWXX`mvuunrzn]}|dsurstqtutrssssqstsuvsturuvwxjiȀvƲ}{˴õwurq}||z}}xu{uekfYu~H)++.61110//13989<:69@OKDIRWWO@6699968;=?@@@?>CA@BEJLMMMQSJJUQIɝ{pȽľ¿ӿxoxvmumt~`ct~|֖gieUuɓairhieejkjjhegfmomifehjiggffgilkiihhjlmkgddgijllihjpqod]]XTM>9WjZ<* $&''''&%$)**+-/0/40,-473,'!4d|x~yu||rgllfkmonps|lWJ60887875776=>1513=<.IfM3AVfjecfM?FLU]SPbxs`XvbPzimqg`eaYcowwuzwf_fmmqrmklnmnnnnnnnnpg\vþ¸ļiqplmljkjjjkklll\UTY^gptvvorwl^zcstrrtqppsusqprrrvxtqvtvsvuxyjiĹsï}}ɰ~ty~z||zwspvh[xsJ/*(+.0/.03565668;;989?EDFLQUN?679898:=:965<=5687BE>?EKH528:7769954:@A@@;>ACCDFILIMSMLRKUΝ~zq|uvux|vjokkminx`vՕgjgXx˭ϗdjrhjgfkkkkhehkkjjiffjkihhhigfnlihhihhljgeceimmmjhimmj_[[TPL>6QdibM1$!''((''%%(),.,+.328:3-,+(!!9fvu{xtx|}|ojkfgplfqvxvw||x}ul\IIQONMQS[^RQSTZTM]hZ`ehfdeggO@FLT]TRcwp\UueQyhote_fc[douvtxte_irupmoqpnmnnnnnnnnph]wþ¼¾ȾiqplmljkkjhggilmaVRX`jsvqvtuvh\vewvpptptuqortsuturmrvrrwtssyzhjtŰȫô~z{~{~~lefZ@2/+)((//136788;;<;989<77233277--587657:99=AA>;>?BCEFHIJHKRNKSSP͜}siĕvq}zmrz{vnphf`WS[juqz{ӓfihY{ɬʔcjsijegklklifikmlhhfcblljgffhjnmjeegiihigc`cimkkjgillh_]]VROA8TfdaaL2"'(((('&%((,.-*-32892-,*%%Agqr|x}~yx|{nihdjnff{sptwvw{~smjcg_cfik^UPTXRQ]dcdgieabfhPAFKT]UScwq]VtaO{impe_fc[douvtxue^fnsronnpnlnnnnnnnnpi`x½¿ǽiqplmljkghijjjiiaVQW_jrsuwrrth^{esspswtsrssrqrpstuwsrupvtrrxzfhoı̭ĵ~~}syzuxfN8.-)%'0/2565568645:<;<=AEFGNMNSMGPVJfǘzsj̏y{}~jev{svunfQFHFDGJZlҒfihZ|¦ďakujidglmlmigijjjjhcaejlmkfegjjjihggikhlkdaflohihgillh_]\SON@8Sk_XlfA$'(()('&%*'(,.--0474-*-*#(Hinp~zz|}{|xjihfjg_k{oluulq|z{||hXXYZb\RLTXOLSZeeilkfbcePAFKT]UScxs`WtdQyhnse_fc\dotxuxte`kuqopqnijpnnnnnnnnpjbyºýŻiqplmljkihggghjk^ROWcmqotvqsxk]fturrttvuqorssqvtqrrprovtqqxyejºǿb}Ĭӻ|yqljnnkmj_ZK2%+,+02454248=8:<<;:;=:;:63310/036765679;:;==>AEHIJKLOQNMS\QRŒ~{tkswxw{ubblvwvpcUI@><8:A7M̿ԑgmgXϟdhqjeechjjligjhkkiheejnnmjgfhkjhfefhijkjgdehihnlhdejlkc`\SRK:8NgbYcvi3"'&'/+%)%*+,,,/0571,/.*)0Tjkr{xtxuwrcac_^\]fnhgjf`iyurt}fLIWb]SNNSQJP_efrlgfdhkN;EPW^URdun\XygUyhkpi`eaZdotxwwqecltimrpklonmmmmmmmmth[u¼ŷȼhppklljkkmihggie\UUV_ptruwusyi_xcpqqrronoqqoqvtstsoqttwtswstvefÀR~ͨѹvqnovy~xRKRG1&+/-.0367557:79:;::999=<7468721475249:<;88;>@==@CCBFMKGELSSQOSLsʓ}vkܡ}owxvqeecnxtnh^KB<=;537;7^ԑfldTzιΟdhqkfffklkkhegglnlgbemjkkigefgkkjgdcehhhfddgjjmkgdekmkeb]VTM=:PcaY\sxP+*('&&(*$+.-*(-1351-//*(#5Wmou{ws|ssr`[]\WW[fa^be`ahjmry~oYT^_WSQRRQX_bgnheebfiP=FOU^TMavq^WvfTxhkqi`eaZdotstvrfagnpkjosmknmmmmmmmmti[tŷǻhppklljkjmihhhjg_UST]ntuptsrxia}hvurqpoqqqqrrqrsrqrpqutpnrptyjiJ|ˣнzRPVO=-/430147987678899:9867=?:7985332147986:<;::;<>:;ADBDIFFHJJJNSUHXЕwlʸqjmkjjhidkpgdbXE=87611489>:586324984689879:9=99=BCCEHFEGIMOQVLG՗|xmŲ|qhggcdlic`ZVabQ@956644686<;LxӐejbRwŲ͞dhqkfgjonlkgbegkkijighkjjihggfilmicbfkilkfbdimjjgeglmlc^[VRM?:Pba`etq]4)-(%&.*--***,,00///+(+B`sx|ztztmoof[]]X_baS]ghhomagdjzze`giaRP[abi`a`hkfggM8AMW`VPcun]YxcQvimsiaeaZdotxwytf`hqupjilooommmmmmmmnf\u»þŹŸhppklljkhliiijnk]QRYbmpsuupoxi]~xevvrqswsqstrqrosxvppvyxsqsoptdhN~~ŠؾîW^hsucPGC=:9767788889877:<=<<==<:87348956;:96569987;><;8678986898679<>>?A@<:::77;:65899779;957<=9;ACAEHJJJNPQUXEq՗|ylſǷnwpc\agc]SVIAXlZ832146876<9NbYbwqmpgV@/#%7J9(*,-1+*-/21,--)1PjtzzutvcdmcYXXY`ZP[gjlqlfkghlrx|{cg\OMNKHKPU\hifhcS?IRV[QM_ur_WtcQvhkqi`eaZdotvuwte]fsolstjkqmmmmmmmmmof[tºŸhppklljkhkhhijnj^RQU_lqsnturud]}fwupquqsssttsqqtsqsutsrqt{wwwddȿ¿Hv{{zxwuuwvxsko{}}{}~|~||yy{vrĿżýþʻ¿¼ĺ̾Ů}~d_ovaSF?<98::8578:::99:=>?@?=:89999:;:8::878996::98=CB>DFFDEJOPUVDsҔ|xwmɿ~wtme_^`\SPPCJilJ0740/1577A38AEJNUҎdjcSyɳ̝chqlhhglmlmjgjlkhhkjijkkkjhgijmlkhffhjnomhdgmqppnklnmid[]ZTQG?R_[lwccudUK=)+DY9(./+/,/0130+,.+7Ypuywrt}~rdflcYXY\]XX_cflpicfjgktvt{fWPSVUUTSZbifflfQFQRR[SK_ur`XtdRwfjoi`eaZdotqqurcZcpsnpokorkmmmmmmmmqfZsƺhppklljkhkhhhiliZRSV_nqprvurve\xcvvootqpqtvtqorttutqqvustytstaeĻĀM{~}{|~}~{u|}yxxy{}||~yw|x{}utƿѼ¿ȼȼtv|xy}shpzfXJ=;:;=;729:<==<:8>><=?A@<9:<<978;998667768:<<<=@B=CEDDHKJTQAvϑ|xjzz}qc]\URTVTNARuh?/7643465318>?HKEKyӿҎdh`Ou̝chqlhhhmnmmjfinnoqofchjlmljhijoonkiiknttqkhjorqrollnmibY[XQOF=W^`wvV\wgKGK85L[=&+..4.-2241*,/,<`uwxuptwidhfbWUY[WU_[Z_jolgefjmnoui`cccc_[bhkfhqkLHUPO^WLato^YweSwfhni`eaZdotsrvte\ftsmospklpmmmmmmmmqfYt¾ƻhppklljkhkhgghkh\TSS\msrtsooyl`{gzypnqutpmpttoqsurpstorootqsvflÀK{}}x{~{ӻκŽÿ~q}}|}v]^]B66<:895777:=;78<===?B@<:;<:9:<<98;:67:96779:;<===@AGIEIVJBΌ|m}uxi_`\LISWNOK\nS3852456;=6278;DIIJ\͔jncOy˽Нflnigfiopkkomgorqnmjghjlnmiehlssojimppsurlhlruuvtolmljd[ZYYRDCSUdrNWqgLDJA?R_>'++-..-,2..0..(::::<=<<<;>@><98:==<=<:869;;88899::;:9>@?ACLUEUϓ~yj¹½vmrc`XNJQTNPIJY\E3976/.9=72/128BHJOOuҘjm`MwӵΟjqtmiemmnpomlknusnmnljonlkgegklllnoomkouxsmmtzuvuolllj`Z]ZRNKPN[pzbJVnnQ?EHLY_A(+**-/003400*&4ayyvwpty{rhdedZWY\ZZ\[VY\ahkhbafgipwupdECY^NDT[WZfh`SDOTV^VQcvqaYsfUyjnrgbdbZapttrusfahprnmmlllijlllllkjnhatļþ÷ùhomjmnkjhfhjhjml]USX`krpnrruwb_hevxrpuoruupnqvpsqoqrrsrrutqtrimN{ֿ˶ÿĽrzuy[U[TWQ;-26369::87:=<>B>::>>:;?;<:7898899999999;49?A@?ILCuÏzj|Ŷmiqd\OL[bWIOEMWK95721)(272//--5@BFONVӗgi_Myϱ˝hovsrropqsspnmrwwrrsrptqpnmjkmmlptustwyyuniltztwvpkklkg\]WOPNLKbypUFPgpU:;HSbc?'//.,..237..*%Hw|syxnwz|zlcaa`TUYZY[\XWXY]did]bjkfnzxjusPDKNIJHO^ffglS@KVZ\RPduo_ZtdSwhlphaba[cprxvuqgafoommnmmomkmmiimmkqi`rµø¶homjmnkjkhhhfhlj]URW`kqostrsucbgfxxqorrutqqurlptvutsqqsrttrtriqz}|}{|R}|~~zмȲþŷwn~su|[V]]Z^M2*020668759==>AA><>@@:??=;>?>=>;B=9<8999:;;;;?;8@?;K{Đ|zzn~Ĺnc`^CJjp]QOCGNLD=7.-/3/&)/.+&(6?86@MC_ƾђad^P}˶Ϧorzuompwzwvyvnrtwyyqlnqruurmnrsvyvqmqvrwzwpnrywywqllkj\\bYQSL@PwwURQCSgW:.,AkrF(/1-+//21,+*#@wqy}rmp{|~shcVNTXY[YX[^YY]_]\\\[]fmijvzmis|hI?IPLKSbifglT<@HRZMB_tq`WqeTximqiaa`\dpqquvriafrrmpsnhkqnkjjkjlnqf`w²ƼŸhomjmnkjfdhjijli]TRW_jqoqqnrxfafcrssuuqopstrrursuurqqruqrssvsgkwwmiimtpqssrmifnmosxywsbսxɷɮɺǿu^`hw|\X[W_[b_F358<=<==:8=AA=:<9::;;<<=B=9;A=Jq뾇|uti{·~jXMNDXtmWJB@CA===6/062,+)%&&#'5=74^sqaVpcSv~hkohaba[cprvvvpc]ftrklqolnpnjilljjnfc_q÷Żƻhomjmnkjfehkiijg\TRV_jpopqopsaadfuqquurttppsrmqprpkkosuqqrswsgjyxkhnswwvtuurkelmquwxura~Һrîȫ˿ǾĻghty}|{{|hU\d_b^`bVA406;99@>;?D@>ADCA@:>A@?>>>>?AB@??<<<<<<<<<;9;9>V껊uqshyɹveWMQVa_L@EK?:668972374020(&'%'29418>=Cqחeh`Msœزuq{xrruwz~}yusuyzyyvrsvwwvsnorzyyzwrsvxwutqorw|ztooplfg_ZWYOER|iJTVF>eoJ2*,VrM'-2/-0,.100'9mko}tipw}tj[MUc`USVZYXZXYYWWZ^^Z]bliepvihq~~tt~}zmMHQAWkrrrqmO=EKUaWL^ro_WqaPt}eimgbdbZapttqrnb_jutomnmmmjkkllmkkkgb\nļ¸Ƽhomjmnkjigiigghf\TQV_jpnlpptwc`ejxrpsqpprsrqqrolmqrrsqvqqrtxsfkvuow~wnhffnqtuvuvv[}|{иs̮̽ʿȺ½dgy}uv}{w~x[Y_d\`d`_dYA09@;7@?:>>>>>ACA=<=>?@A><>?@<8=A=>>>=<<;;9>?5L㽎}xwxgt¼mc^\g]J;8?BA@>?A<9@AA><<=;78?A<:>?;8@8@w¸縊wrsiwżpdfrdK:3/:C<<37=849<=?DJONHC;2151.01:>JtҒagbPtƙʪvpxsosyzy{zvtw||{|ytvywy}{tswxt|{pt|z{wppyxxtnkmnld\]\SPouYSYT=3[zb?0!7fa.$86++2.4-&8twmyjivugcfdabbOO\`ZYWVTYYVWZ[\Zhun[bshZhzzff{~|xg\gibhplnsoW<>JW]QP`pma[scVxyfnrjbeb\clovwvnb`hnnnmmnnoommlkjihhnh_wŹjnnigikjilkgehkk]USV\fnnspnruechhwrouxoqrssrqqttprxuoqrstsrurgkwq~m[^imruutuvvS}ǯx̯øè»|k~WW_`c_ddccfhaW?58=9<@9=?ABA@@@@@?>=;=:754>@NzҒagbPtȨvr}xtv||z{ytrvyz}~wqtzy{~zsrx{|~}vsv{{|xqotz|yytmjjjhf\\VRonXVXTA6UuiF4%0]]5'/.,./.,*8]vhydgyyrfadb_``TOWZVWVVWWVVWWW[^fqpbdoi_etyhdxurwtcfddoytoppV>?IW^RO`pma[sbUx}gkmibe_Xanupprnc`hppnnqnhjqklnonmkjof[u½»Ź·ùimnjhjjighgfimlj`UOU_lqmlnqvvdccfwrpuwtonrsrrttrvxsprsqrsrrvsinvpjX\imsupnrtqHtx}ohmq{~~{|̸vȬɾvè}iZfaZcfjjjhijihY9/;>:;:???ABBA@BCDA<<>?==?A?:9;B;785//4;9H{ⴈupqgs}mojRB8431471,0;@93:CJMSZ`a`][QG@=AB<:CaҒagbPtǩͫyt}ytsz{z}|xww~~{|zutwx{~zsu}~{{}{ss|~xqnoruzzvolkkhf_Y[mbX[WTH:ImuR:)(Q]=+--+.,0$4Wrmwgp|}yoebcb`aaZTWZZZYW[\XSVXZ[dhigcdmjdalwiat{vpz|d\aeoxvnklU?AHU_SNaqma[rgUv|ejnd`fbYanvqqvui_dmomlmnmnoijmopnljjaVpŸgloljjjgfghhjmkh]URV_jrrpmkqvgedhysorrqqrqpqtwqpuvqtwrnorrrwukkvwxhZZclttmmvyu8[jd`b`UTY^q}~ësǩƼþ»zsaZbcaehlkjkjklnmS<:B>:??@AAAAABBBFGBBC@;;<><;:;>732.,3>@;gݱwrsgr}{u]C6123012..3;?:9BLSTUY]acec^WPNRN@9OtҒagbPtǠΫzv|yrv}}{}{vtw}yzytswx}|ttzy{y{|xuzztrppswyzwroqpnf[_XW`VUM=ACB@?BEIEGJFDB<=AC>97;>=84346;BC?BDB?>BGHDDGGFC?ACC?>QթytugpmTN;52/-+,++-37>EHHLPTY^aehkjihdchcVV~ƸҒagbPt̿ɤut~{{rsz{z}|xwz}}~yswzy{~ztu|~wwspz}}|wokq{yzwrnnkg^xkPW_W\R6'Q]7"DlT&!.,-))Nrwboymjyzohcdaabbacf`Y[YUWZ\ZVVWWX\cZXko``jf`cnh`m{ztx}{}{mkoqojkkkTAEKU^TRbrnaZraRu{dkrh`b^Zclqrtwrc]dnsolmmkijspmjijkmrh[súgloljjjgjifceikiZRNT]jqpppptucbegtnptrprrrqrrrostqqsttrrtrrushpozvso`Ycmnnorvwv3]h^^]XTVU^s}|}|}ḢtɿÿdZ`ba`^Z_`bgjropnjlpojpuyqW>;EABBA@@CFGIHGHFA=B@?@DGHG<77=>>HVҦvrrdkX?>-.+(&()((/6:?ILKNRV\`ceilkjjhfig^kҒagbPt͸̥wu}z{pv||z{ytrw}}y{yuuxz|rqv|z}|wvvyzzuonu|xywromjfgeSY[X`Q/!GrD'>lT$%<63-AfxpeonfmyqfddgecfedghbYZYSVZYZWTVX[^]NRlmWZje`coi^k{xw|}minohdhhU@DMV\STcrnaZqbRt}hjjc^ca[clqssvse]guqmiinqnipolkjklmsh\tºù¶imnjhjjihhfdfjjgZTSV]hqsronqsddfiupormnssnlnporttoottmoprrrwuklr}}sssaV^inprw}|w;dmbb\WZZ\hy~||⸡}vũÿzh[YX\a_[[abjnflknmhjqrnquvzpL7BCB@@ABDECKLIKIB=@?ADEEGJC@DIBA\Ф|soo`g}P;5)/,((,.,+5<=?INPVXZ]__afjjjlkhig`aҒagbPtģɣvu~}tsyzy{zvtw|}}zuvy{xpr|y|zv{~zyvrpruxvxwsqqnk|bUYXYcO+@M+8lP#5P?53_v}|ninoioxria`db`ccbemdVUUQW\TTUYZVV\XNYur\\hd_cqk]i|tkov{whjqjeikV?DNW[RUcsnaZqiUqzjlhb^c_W`nwusvudX`oqljmnkkmjjjjkkkkkdZrɹjnnigikjgklhfffd_UPU`lrpinswtacdjxssupoooprtttspoqqpppstusquqgkwzy~}yyuaXeqsngkx}z'Fmswinwmoywkeddc\ZbgfdmbSWXQXZRSWVTVYYVUXim\ZgcXbmi`dtztqpqy{lisndecT@DKT[QQgwp`YrcUv~gjoa]c_X_krvuwrc]eqqnllnonloljjklkjpi]r¿~emmikkijghikgekh[QLQ\iolqoouubddhyvttpuooqppqppoqsrqstrprrrsphmyrp~vu{vb[fokimtxyxAiqefe_[filu~zzz̸~wÚżļ©rprjjgeilf\Q\cehklnnnmoppnlntutwxdJ=AA@BA>=GIHHHEBC@ABCDIMMDOGD_ʡ~vqmdkaC;831,''**+/17DGEB?KJIJKFCFFHEBEJKKKKKdͥ~uqogmrO@:62.((')./18=?BGNSVXWY[[^beeegljfgigZn¾Ўai`Q{ƟЧrv|xwr{{{~zps{}yzwtvy{uquzw{ztsxyyysjjsy}wuumioUX[[Yd\0F_:*PjUS|_/Bkwzt`Uakmtupkif_efehhda[TLV\Y]]Z\\YVXVPNW^kmccgaX^ji]Ygu}uxtq{xfflghjT=?HWaUPctn_YscUv~gjoa^faX^ltrtwob^hqoommlkjjoliijkkknh\rŸemmikjijkifhffkg\RNS]ipnnonpsfefjytqrnqpmmsrqsrprrnknqorsontqdhywjt|{vyzdVblomhgpzFovilojcgiku~}}}x{ſǽŽþƺoksplmjkopnotcX\jkiiinoonllnqporvw{ypT<;GFFHAEEGLOJGKLMIGMLJOPJgЪ|tqohn_MG;20*,/,,201;AAAFLRRQWZ[[^figdgijhhii]hļ¾э_h`R{ȢŽӫtv}{vrqz}|~xow|yvz{tp|}{qpx~vsz{utz~{ztkku|zrtshorUX]^\kf7E`>7ZiX[}U5Xxs~iVCOW\fliiifbfc``]VRPLJUZWXXVWWTT[[SQW^iibcf[X^in`QUruurnt}jfnkhiS?@GT_SNarm_YtaTt|ehma`gcY`mvuvuncbhnqomkiijklkjklljhlg\rŹemmijkijjgcffhmh[SOT]ipotqmoredfiyrprprsqoppqupqrroqrqrturqvsgn}{ljxqqi[hqrlehryyEvpjjjiddit|usz~|z{|}}}}}}~||xty~²uyŽĺ|¾¿˻}hnpnqplkljhlsiXXflkmmmnnmkknpilou{xxpLAFHJKPTSY\Z\bfffffhkjii]bþʎfk\JyŖĽԧqxxsuuz|yy{ysw{xu|}vq}|}~{srz~~{ywwwxxutqlnv{xsvqi}tTUZ\]rp=?yfFCave`oNGkyvnbTAILJNQRV[\]ZPHKMJHPNOWZWY\TVVST[ZRTUYce`dgXUYem]N[z{|{wmlu|qkrmgeUDGJT]SN`ql^YsaSt|dhma^faX_ltyqqod[ampomjihiklkjklkigke\rµŸemmikjijifcfghlg[SPT]ipolfelsegeiwrpsrronmmpttopsrqpqoqpqqprofjwzpkzys|f\ejoneahoqP|lhkkhigjwztw}{|}~~~|}}ywy}zuyyv|~}}~szȽŽ•}¾ĺmipppxruslijifocaillnqmmmnopmkirsswww{`BISTURRV\\YZ^UWXWW[XPTRS_bbffZTVelUIa}~}|wrps{wkljhhXILNU_UP`ql^XsbUu}fin`\a^W_jqsprqc[dsonmlkjjinkhgijjjid[søemmijkijigfhgfidZRPT\gpoqmksub`dhwpossmloplorqrqsuqorvtqststqiiuxtowwr|]\ljprmkpw{Nynorkannnv|z~yvvt|ƘĿȼ{kmppqprutnlmicnkhhjiimonoprqomqsroquwxzqTAFEAIFGGHKLLOKFGGBENTn㶈|wutknu]YI941,)""(..1;BECBEJMLILPW_cdgighfhlhej`X¿͌`h^NyǙø¦us~}oqw|}}{uov||{}yst{z{~vty{xz|srwxyyslnuvw||nmhTKLQ[I-Okp]TgxbQOntrlacdh[VY\UMNNHEHHJSYXVWVY]\XUXVVVVWZZVXUU]acf][VUbnYI^zwyysoz|leejkWGJKUaVOarl]Xr_Rr{cfke_c`[dnrnrvob^itrnjijklljijklkhehc[remmikkijbdfjhgkgYRPT[footqoqrceeiwpmrsnpsqonnlnqsqosxxqqrqpspfpxurvz|wwWWmoomnrx}~Dwqnqmhlpor}z||~y{ȸÿ˾λ½ſúmglokntrsmlopljkloljoomoqrrqooqttjilmtzy~~hMGHFFFJJJMMKKQNIKOKRho{ű㴅zyvrghjZZH50)&.'%,2.1Xe^_bflnihmbV~ĿˏllVKw̦sy~xuq{~{}}wqrz||}xpqyzz{xrpt{}}ztsuzv{{qjpwut{j}|rg_[VfV.''4Seafpvdctmof`cWRTLScdSKUYPSUVWYZWTZZZ[[[[[[VTUW\ZRPTTV_d_YUWPWigOPk|~|xqqv}}qcagT?AHS\RQ_on`]rcStzdhlg_b]V_ksvture]ervpkiijkliijjiiijjf]t{`ikhkkhiefdijfggWRTRYippnooto^gdftqoppoonoruroqqppqttqoooprxsgjyz{|vtwuqqwq`\hsqoqv~Cnymmnopti^lz|{|nqvy;|ʽürfdekpqmknpqooooppqqsphchopoomkknprrnoqppqtv|z}{fMDHKJHDBDKPLJMRSWh|rtytq廃{{rpdeseXK<30..0//**3=CFKFFGHKLIFNSH?L\[`efgkgdkdUǿľĉgjXO}ʽŤsu}{wrq{~{}}vps{zwxuoouvx|{rouuvy{xtuyyzysmpvwu}tjoincb`n[2$',8QdmvwZi{j{ya\[JFSZ[caTQWWPQWWUWXX[VXYXWVX[_WTWYXTLQTV[aa\ZWZQUgkSNgyy{{wsszzh`eQ;>FS\SQanla]qbRu{dhjc^c`Z`jousvrd[cntnijkihhlighkljghcZq{`ikhkkhifechkghe]QPS\imnmlkrq`ehjwtopppppoprssprusoqsptrrootsjg{~{|zxtxtu{xe^hppnlr}@pzjkqpkti_mzy||y}xz}ux~dzϼƼzgghmojopnkjklklprrpopsuslcdlpnmpqnlnqsnpppopsuptvyxeMDFGHIJKLMJGINRWdrwrz~to}纄~{sqef{ttbJ<;630'-/*+9@@DIINPJD=515BMLHRaed_bkgdm\R¹ʍgiVP~ȷy~ævw~}ytq{~z||upu}|wuqnrqpptslktwvxzxwx||zwslnv{{wqqzyeiqcehpi9"&..>Yo|q\ntpsa\WHJ]hcf`WTWTPU[XW^]UTXXZZURU[UQQX\\VRRSW`gaZXWZQSdoYMc~xvy|ywtu|zlbeR<=FS]SPaom_\qaRu|egic]a]Wamttosr`^ktrljmomklmjfhkmifmg^tödmmijieeggbeffig[LKR^koqqnkstdidfurpqrrrppqrpnmnrsortrrsrpottlly{yvzwuw}vb[fptspvEuqswtmyn^e}z{yrszzv|}zǹûibfjimrlonnppooportronqtoqpg^epppsspnopmopqpppsusryysk_PMLNRSPMMKLQUYdpurtyywqh鷁{sqegxx~jNAC?<4$#&'0@FDHLLPMB;96999ESPP`h`VWZUT_ROϏggVQ~vw~ztq{}z|{toovvsqjejqonpoggpuvwwursuy|{rjnwz~zuw{i]xuehlltB"!/13Ksriqxhzi`]WMS`caicVW]\VY\WU]\TR[WX\\VX_YXY[\XROTQS]d_YZUXQRanXG\|}zyyuwusuyqecV>?GU^SO`qn]YsaRu|dgic\a]Xantqmtq`[flojghkklmljjijiignh]rµdmmijieeegeeecih\VVSWejipposp`jghurmnnmnpopqrqpmorsstrssurqtrhnz{}xy}{vuwq^[houvu{Fq|tuupmrl_g|s}wtxqnjbi~}z~}}߸acfjkfhkjhgjnolmpqrssqppqrqrhX\lrkqsqnoqqpqqqppru{vuz|vpqmdZVVVQMLKQWWWduuuursuuqtw鵀{srfhyr~lVJIED>/%(.9FIHJSSSK>9<>?QMDNSU`h\PIB>DOLQ͌dfWTŹ{pv~}wrr|}z{{tntvsqrkfkonnqpihoquxzwttuxy{ulmuzyyptzZrylmnfozsN'*2/Coto}zoz[VSNMYb`af_WY\ZYYZURVXTUWPOVZWUVUWY]]VNMVRS\d_YZZ[SQ]lYFZy|yxutwsszug`W?>GU_SN`ro[XsbRt{dhjc^c`Yajonqurf`foonlklmmmjklkhghikdXm¸{`ikhkkhi`gghedgeYPPRZhmnonjpn`hfhwtrssnrsplmorsrstsppstuutqsnbk~~y{||{ztrwsc]iptvw|Gq{qturoqi\e}x}}|wt}ofaZ_wvozxx{ÿݢt{ztuj|y_dgeacijkoljknpnmnlnprrqppwrqm_YeskorpnoruqrsrqqsuywsupwrmjgaWOKINXWS^qvpswuuzsmq|·流zsrgiyt{ndXLDEFB76=FIHHGORYXNGDADZYHDITdfZQG>HX\HY½͍fiYQ|ɽyƷßpyyur|}y{zsmsupllifkkkiihfipmosvsptzzwwvnkszxwnu|wv\ivqqldfmmZ1%-0=Xqy{ulVPMNR\ddge\VYVRT[XTU[[QFEDGNWZYWPPT\`VMNTSV^c]VX_]TPYj\JXu}yxwtxtt|xiaT;;DU_SNbqm\YqcStzchlg_b]V_lsuuul^Ybkjnoljkkikllkhgghng[p¶|`ikhkkhiafffffhc]MJR]ilnrnjqtekeguqmnnpqqpqqpnqtsrsppvqpqqsvqdlz||zz|qnqxud`mxyz|BoximutmldZh|y~yzud[\Y\r}xruxx~}žſnq{vmnfedagoidnkiimswvsrtutqpprtoqvn_arvrnmoponrssrqqsv|qiowz|ssssrngaXPS_`Zbsyvv{}xv{~rsts㶂zsrgivysrdN@AINFAHOJHJHMOVYUTTRKPROICJZj\SF@VkeEa¼Ňdj[RzŴy|›kw~wsr|}y{zsmltrljgehklkihefkmmszvoqz{yvpknux~zy}sfuosqjefhib;'$(17>X{yu^XTU[^^_^de[SY]XVWVX[\[RGINRTWZYUWRQ[`UMRQRV\^WRT[YROXi[IRp|wuwytt}yjeQ88BT_TOdpk]ZpdStycima\a_Ybmssqwr^X`bjppjhlnlllkjihggng[pŶ·dmmijieeegbbdgidXQQQXgmmklmsq`gfhvspqqpqrppqqqtuomttoqsqporvpbf|{}~|wrsw|vgk{xwz~Gqz|wolscXd}v|jqpb[agit|xkrv{{x¾ݦ|tlqqojgfhlmiellmprsqostusomorvvqqskgounmsrqvqqpruursvkRFQ`ntrrw{{xtqokklrtjfousrrstrp~urttw|roceu|wbgk`LCFGENOIHPPIFJLS[]Z[^TPVUKFJXhZ@5.4RbQiҿƾȆcg\TyƬ|ʠp{zwvr|}y{ztnorpjfdeikjhmoc`mpstsrsuvuwxsmqy|yyqyvy{mjibolE)%)/3-W~~tZWUYYV^gcabWQ\]WXZZXW[[WVYXY\]YVVXQR]`UMLRQW^]YVTZXPMXcZIRm~|vzzwuuw}xgN>9BT[QO`pm\XqcRnehlf^a]YblprqspaWaoprjfoqllllmmlkjipc[qøųzflieijhhgfeihgjd]TPSZenorolpo`idjwppurssplnrrnqooqrqprqrurosskn~|~{{~uty}{~Cr|zurssdYcz{zyfgm`Zbimv}optuy{}żᩂrure^fhijihghqmnsuropklmoqrssqxtossnmqroqqqtsututqwvhQE@DIU_air|}zyzqtuyzqlr{yusrrrrpptxxvw{}|sqdfog``caUF????OUQLQRIAIKQY]^afb]_YLDER\TD?98GNQw̽ļľºɈdg[Sx̮~ßqx|vtyywxvqonqpnlgcciliijeclnnquxvspwxytlmrtxuo}mmlnalqR-!*/18g]SUV[]Z`d_ccVQ[]UU[XRRWYVTZ[ZYZ\[WVRV^]TNOQR\e`WUXRWSOZf[FQm}xuyvutu|zoT?9BS[SP^pn^WofTlybimc\`^Zdotusto_Xcrolmmgghfhijkkkjij_Xoĸbjkillfeacehfdjg[SORXdmmspkom`jfkwonroopqprtsoppnotsqssqqppushp}}~{zxxwDmytyyrood\g}y~qgjk^Zcjlwqcaovyy||}z{migcbbhmkebejljlqsqprusqprsrqtusswrigouomrststpwxttjRFEFCAHUZ[cpy~}~~vz{zztpqtttsstvxsuwwzytn峄}usfgz[Z_ddWLEA@GNOYYQTWNAJLRX]_ejfa`WKDERZUIEC?CCWŻƼþ¸ʊfh[Swʬ{žqw|uswyz|uoonomjifegklhfhfisnpuywstyxxwpiluxyqht~jjkftb5 /-.IgQVXY]]\ac^b`TP[]VUSVWVVTSU][XVY]]YTTY^ZRORTRZbZMMTYXPISb^MSmzz|vyxwtty{vZ?9DQ[WP`ol\YqcUoychie^b^Yblqqpsn`Xanpptpgjneffhijjjika[q¸ðwekjfjjhhfghjfdheZRORYdmmonlrqahdivoorpoonnnooopqqpqrrrmnrsuwpbm|{wy~Gmtoy{sjsg[d|~z{}|roslbcnlhxgQakswyz||}m[ht~mbcec_ahmkdadikoqqqrsrqqpqrsssrtomrtkbmtpnpqsusmrzscTHFGGFCFMSSV`nz~~}z~yvvtsknqttvy{}zuux|yuov沇~~vuhij=RkcVHELHEO]MWXSUZSFJNSX[_bf^\]ULEBM^YOLMKIHgüɿʌhh[SvȬx}Ɲn{zvtqx}zplqsrojdchljimkcbkroquvsv{yzztmnux}xqh_yvikgrm?$1(/cwUW\^][YZacaa_TQY[TTWWUVWUUX\VRUXZYYTVZ]XQPRRS]g`TQU_WOLS\ZPNhz{sy|{wtw{{]<7EQ\XOdngZ\veZswdkje_b^W_iorrsoaZcompnjjnlggghjjjiine_t¾IJzgliehjhidbafeegb[SPT[fnollmusbfgjuomomoooponpsrnpqosuointvuunbpxy}GtwwwtqviZc~}w}~xqpiep}sburgSmlmrvz{zw}}}}ҿþbD]~jYajhdcehjifcdfknoopsutpprtvuroqqopvsh\irtsolswurprlVINKIHIHDHQVV]jw{~xux{xrrrrsuxzzurtxwusrkr׶}wuiic,>\WB7AQKAJXLQRORVRFEKRW[^`ac`[NFA>J[ZVU[ZQNпǼɍihZSvˮxěnzzusoz}{rjmopnkddjigilja`gllouuqrw{|{umovy|ztnid|yajrqH(*$?_R]^^^ZW\acadaXSWTPQWWWWXUVZ_YVZ^^\[VVZ\XQNPPS[]WRTWZSOQV\YOKczytuz}yvw|_;5DS\WMdmgZ]vbYuxejh`[`\V^jqqqtoc\dmokegnjgljjkkkiihjb]s~cjkilkgegedhggicZRPT\fonnmlqq`ghjtpmnnqppqomorllqrmqwsoprpptqfk||Fx|wutsuiZd|u~|~}rkj``qu`o~yZ`unntxtqtxwwxyz|~ӿVQi^]dghkkhhiihfedljikortupooruvspvqovysf]jppsrpsrqxrmk]OSPONOLFIUVX`koqw{|{wrw|zyupprvxyssuwvsqrqwiw}|vvihi/-FXH8?QIAKWMNNMPVQB?GPVZ^`_he\KEB?KX[[YbdYXϿøǍjh[Uvʩunw~zrp{{wxyrlrqoolfbfgiihfgimimtwsnqw|{yqjmvz{}v`jlhrpQ- *^}`QU]aX\YY`d`]c_XUUSQSSVXZXST[YYWWZ]ZUWVY]YPLMLV`_USTTTPOPT]^RLbw{xowywvw|gA5BT\TM`nj\[r\Vtzdhea\a\U\homorob]birlhkljinkkkljjhgja[qŵwdkjgjkggefghdaeaWQOSZfmmmmkrqagghtqoprsrrrpnoqortsstvxspnnrwrdizڀHy}{zuptjYa}}}{}}}skjjbbo~ykv~WUr{rtwqkpwstvwxxxxz`xrc__`bjlcijjjigedqomlmosvwtrrtvvtttrosyrdhqoovurnotojpm_URSPMMHJTTV`ikhmwssxxtw|yxsoossoiepzzutvwqtrp~|zuuigo50LaT=CVTOUXNQSRWb[DBIPSW\__bd^PMG>F[^^Ydj`e̾ǺŌjh[Vxƥqnw~zrszzvxwqoopqsridgjkmkhgiioqsutstv{{zrjkruuyrkcxmml]6>dMP[_bVZVVae`^a\UTWVTVYVSUYVUZYZYXZ[ZVXVY_ZNHJNV]YRTZZRMORT]^TK`v~}tpvvrqqwvM7@SZQN_nl][p`Vqubjle`d^U\gntsrk`]gpnllkijkihhijjihgnbZpxglidhjijefgjgejgWPOS[fmmlllsrafjjtrooqlmoppnnorqmntsoprpoquyrbl~ހJ}ytng[d}}~vppojqmr~}ZVuiqtrpnstvxxxvuz~tbcXX^`fmkiklkhfeflppiflsvutrqqqrsrrstxzpaautpuspqumdfqvjZWXRMOOOTUV_jlhipqqy~{||wspstm]ONg{{vwxu}lgnu~ztuhgr4=`cM8Daf`ZNNTYX`phKHOSRTZ]]ih\IFC>I\ac]hodjʼöċjh[WyƩs}lzytquy{|}xqqmmllh`_fjggieadikqvuqpsvxxvpimw{y|q}}t^`|fhe@!PoSPXcb\XZSQ]dbbc\TTWWTUVVVYZVU[\ZY[[YXYXUZ`ZKEJRW[XRUWTWMLRTX\XPbrv{wuxtnkkq}W:>SYOPani\\s}aVrxckoc_d_W_ksrqqj^\fnmjjlljiidefhhhhhnaYq{ckkhkkgfigfigfhaXQPU]honomjon_ghitsqqtppnmnopqmprrolntrqqqrwsglՀJ}{}tgY`|y||}~~|~~~|}|ulkmknkrzq}xwwfjpmsloqrqrvwttx|»๝xYV]YWZ\^bggilmhcejnqtpijrwsssrqprupusswyseamtnqvojchnopqpoebgfZWYS_WS`njcgqsutuy{yvnnxv_NOJTdtytrvwoqsox{wtvnj?+HOB?QljTHINS[[ewI>MQQVUV`feYKCEa_MGNNNOKHFIQVRT]\GBB42CLSfhcfkamżſÄe_DEyrdjtolnoqsttrpnrnkklfdglmljiffjnoruspszxtspifjnnji}k`mhfqom{ggkW][[]_^\ZXZZZ^^`d\URUYXVV][WVYWVY[\^_^ZUPVYXZ^RDDPZ[URQNKLMMRUWZUO\lv{~}stuqv|uyuQJXVPcwn[Yqw[Urybfgc`aZU`jrnkonb]ekjlmlkijjjiiiihhglaXpíxbjjfghfgkeceimj`VPPT\fmlmihpo\ffjwplqwpqommqrqmpssqoopnnqqryugm؀Glwwzp\a}~}~}~~~~|~{{~||sotuliZW`_blkmyjlmnpprqpssmlqu{Õskc]\YY[\^dh`TY]adiopmqolpskbdltzxwxvqpnouvxwi^dmjhpssvyyvuwxwrvwuu{xn`[[^[]lznxqfo{xqnwywwzzy}}xvwuruuvupqvywwtqfbua0;G?SnbHTxvVFNQIEFEFKRROPVRBA=))>JT[bhce_jɾƼĂhdCCxzdkuohjjnquurnlppomjedikkjhgdejoopstppwqqpjcfkkmlhxxk[mphsuiowo~^X_Z[\\``XVWX]cece_XRUYYVXYWWYXXY[ZYY[\\XTWWV]`PAEU]\SRURLGKOUWWZTO\jrv{rrywx|zzaPVSO^qiZZs|\Vqxcefa_b\Xajpplnl_\emnnkjijhfiiiihhggoc[sưsckkghighfeffehgaWQPV]hnmjmlnl`j|hnwonqqppoopqpmrtsqopruqprqsytdtրHpzu~}yxq`_v}}}~|~~~~~||~~~~~|{z{{vmiiklZT[knhvxhmklrnnnnqrmlrvzĿѠohaYXZVXZ]bfc\^ZY]dinsrnpxxmcaikpvwspossuyvvwmdlywoqtvywuvz|{xyxwyzvuztj_]\Zbptvkerztmr{~zzzyxwxzywwwsrptxtpu{z{xukfwR1BAFdmfennTPNHFHAFNROKLNPGC@98@FKJTikf\kȾÄkkRNxxgiqg^_gjnqroljlppkfabhfhijiedgmnnrvqnsvtpicfjjpngpowWsxrqvmst~j__d[\a^\^^ZXVZbddgaXSTVVWXWTZ]VW]\[VTW_`[UVWW]`N@EY]XOQVTPFLQVVWYRP[gou||suzwtxzznYUPM^ogZZp|[Vqvagja`b[Wajpplol`[dklljghkkhiiihhgggnb\vŮpbjjfhhfgccedacd_WQPU]gnmjommlbk~hmvonpnmllnopqrpqqoprsrvtsqrwp_oԀCp|pnopr{oaew}z{~}{||{}~}~|y||ztnkhbca\fpnwx~oflnrrquusrpoq|z̾Ͽ{kb\VUXUY]adgfc][XZ`hlmmrutrqj_jorsvxvpptrrrsxxqqyxqqtwuuvxwtsusyww}ysw}se_b_]dupa_qyoirtuwywvxwxwvz{xtrrvzupqwwwtrjet~}P;JAStnlof]UNIAJTVPLMMSRJGJHGNKNLYgh[l˼ýȋkk[X|tacmfaeghjkllkkoolhgfccfhiihedhnmlpuqosvnhgdeiknlcjqXqxfr{uuYY^Z\a`[[^\ZYX[```d]TTXVWZY]ZZ[ZXXY^[Y\`_YRTYZ\]PBCSVSNORPNHMQUTUXSNXepx~}|wopvyw|u^TNM`of[Zm{YUrt_hndaaYT_ksqmom`[cilnlheghhiihhggggj^[w®qaiieggeffdeedgf_VPOU\gmmlmlonajfgtolnplloqomnrsnlptssvsqqqsysbr¾ـBpzpspms|tbe~|{~|~}~~|yvyz|yuqf_cdiiurh|wklnknomqppoptusppxz~Ė}wmc\VW\U\bdefe`a\^\`hghsuuuuqidclrrsvwtqpooqrstxvwyumpzvssuvvvwvvvuvwvtuzwkaacaopY[xwhiwxv||w||xxyyywxyrsttsqpprz{vmlx~}~~gSMHStoi}|gSDAMVSLJKLJROINUX[_YOUfebk˼ļljhfVT{x^cqkcbhikmnmlknnlkjfdfkmkhgedfkhowunnrrlheadkonf^nrRvvz{vz{nYX\^^]^^^][ZXV\cccg`TUZXST\_YXZXVXZWVX[[WUURRW]]M=CRVRQPNONMPQSVWUVSVemv}}yxvtvzrtziZOO_pj]Zs{]Tuuaeh_\b[T^lssnol^ZfphhkjefjlgdekmighkaZr³ɵu`kihjfgeffefdfibTPMQ_jlghjjonakghvtrojplnqnmqsostpnprtuuutswo]sԀJv}qx{xy|vedx|}~}|~}~|~}zz{~|vojdcghcrzeo`qpsopqnnroorttrrtwzɤuqzp[W_^X\[^dfdcee^YTZghjstuvxuoiigmx|wqqusqqrstvsxzwuvvuyvtuvwy|wsqsttsst{}ukea_kkW^xvmuyywyzy{|v{~|xwxzsrtvtpnozzuqhdq{{z}u`NHbtimus|wkXEJTZRKHGCITTNQWZ^bYMN[`epͻżƈgfVU{u~`doiaaghjlmljinnljgccgghhhgddipmoqmijmpnlf^`fjlh_m~yUzy~sc_a^]^`^\ab[VWW\ddabaUTXYXVY[XX[ZXVTVVWZ]^ZV[TSZ]M;?LXVPOOPKJNTUTTURTXflrzz}wvwxvvswr]KK`nfZWo|ZRsucgk_\`[Valpqmmh\\hojghhghjhjhgjlljjmd[sµƯq`lhhifgecdbdcehb\UMNZinmkmlom`ihkvqosstpnonnonqsrpoqssrrssswp_z׀Gu}ptyvsvqcbu}}|~zv|~{~~{{}}~{wpifgecWbnq}pktrimpmpssssrooqvwcuxjY[\YYb^^ab_dlhc]W]ikhmpstuuohegmrtrppstrmmpsruxywwxxurtxzxrqqrtxyyxvuvy{{ysg\geU]son}yxyxz|ywxz{y{|ytqprwwrpquwxxlbo~||}|tbSTTZhqkjw~}mTDFO[]N<;DLRSS\c`ZaYQNU_jrտ¾ÆffWU|p}_dojehfgikkjhgjlkieabgijjkhcdkpopokjmngjlgacgfhk_mq\|y{ud^a_\bbb_]`_XZYV\gd^cZRUWVVW[YVVWXZYUTVXY]_\WWTZ`ZA4AOUQQTSOHKMRSOQVUMUdlrx{~xtsvwtx{dLMcmdZVm]Ttuafh_Z_[Xbklooqj]\delijkkllhjjhgillii`YrŪįr`kihjfgeggefegjcWTQT`jkflmlom_ijmwokoqnnmmpqqqponmnpppporqswq`|ۀGu~ooqppok_bw~~|}~x|~}||}}zwwrikkeda[]{kmgnqghqoqrpoprsssur{έslv`Y]YSX]_cecadifba\[gj`enutrsndajqrrvupsroljlnpxyxvtvvvquyysnnrtzytszztrvxz~~rehhZ_lko~}yytv|xwu|}zwxuqsrsvvsonrwz~sen|{~{q`NVKSon_nxqxrSCFN^cP4-6AEFJVcc\`\VQScrwҿƿefWV|kz`enhcgdfhiihfedgihgcbfijiigbbhmmooifgghjhdainlem`mkb}}~|oebZSXecaa`]YYYXXeqg\b^TSVWXUW]ZWVTVXVRVZ\[[YXWV[^ZH8?PTOPTSOHGJQTSRSUNVcmtxyz}wtvvssvz}jPSenf]Zn^Uutadg^[`]Xbjknmoi]]egihjkjiifhifcdjjgjaYséo`lhhifgeggdecdf_VROR\hjgjkjnm`k~eivqnnmgmqtvrlmppoopqrrppqqqvo^}߀Gpzomnorqndew}}|}|~}}~|{{zvtmghkdcliYuxmnhnmknptrrqrtvwvvpyfyq\V\]ZV^^befdeggab^Wcoc_lyyuvrghgiovxwtwrpqojmsswxsruwuvvwvtrv|wzyuy}xlnw{ww{yrnpebkow{xzuw|wtrx{xxzytoqstvwtptuswtjo{~yx{iKGQ\oi\n~mjt{|\EIS[[RD4(07=NSNNTUPFDNQSWOB@MRTams|{}{zxuuvwtx||_I`mg[Wo|^Uut`dg_\aZS]hmilpiZ[hokhhhfghfjhgijhffkb[sï®q`kihjfgebcbcceibUSQS]hmlpolmj^hhkvqossolmpppqqtqpqsstusstsswp^߀Ists}~|smt}|w~}~ǽ}}{||y|}yyukhkjipink_gvj\Wl~wnrsmnnorsrtvsoxxz~agYs`YeWW^caaeijoi\YZannaZevvpomfeejqvtqutvxuootqpoptyzxwvvvtsw|~zuruxxuztu{{tsxwuidjv|twwtyytwxyvuvyywuurpqtvvtpsqy}ss}|z}|uz{mwwYCEOix\KKPUWXVMCGIPSSVO=?XYSf{|v͹˼ghXSwq\ania`degggecbfjlmkfeighhigcdkegjhehljkgec`bgimd^}tea]Lzum_]`]_e^_le]juinypa_cWVZZVWY\XZYURUVQRY__][YW\[XUZVE=ITRNRVQDIMNQXYK=ALSdos{y||~}wrtzzsvyiPcqiZVq[Ssubgjb\^XS_ilfmshXZgkllmkffjkhdeijfccg_YsıƯ°m`lihjfgehhffdeg`VSQRZejjmmjlk_jghvtrojllpropqosomorsrsrqsstyraހLwuywfdx{w~{{||~źvr}}y~~ywwtqppoljlpifypX\ɣ|vnpomllsrqqsuusrssuy~zmxYlv\[ba_\]adddeih_[\cppeZ`kvztrfcfkmqwzttwyxurpppqrtuvvuuxyxyzwxutwzwvzxutvwvtttmcdpxxx~xx{{zyvwxxyywutquutuuw|yqs|{su~{y}}tzv~yO@BAciGHQSQSROMKJJQYWRRS\\XjzrþÃeeRPxnz^cmhb^cghhjfa`cghggdcejihhgdceenofbghcgfeddeginecyi`YZHwtzugab_[bdmz}mfw{gisldbZXWVXYXWVTXYVTUWWXY\_]YVUWSVZ\TB;NQNNUTLIKMSTSTOAFJQ[ht|{vstvrqqu{}oYbmj^\qy[Rqschha[a^V\dioopk_[clgknlllkhiiihhggflaZqļƭs`lihjfge`dfigfg_TQQT[gmijkhmh\mflvqpoqmmnnnqqolpqppoqurppqtyq^݀Cmwnortupphjz~}|}ųht|x{|z|xrmllgacnflxmztooqpmlrrqrtutstuuz|~mtjg}zia^][`\\bfffhli^[Y^kpj`^cpyrojedimsvtuwvtsutqnquvsprvttttuyytwutvywuwwvvxxvsq{m\[l{}{wxvwzxvwzxwvustwsrtvux{{qpquwuuz}zx{kUUXVa{PCE@TzlKFNPPRRPOPOKNX[WUT___uwv¿ÃeeRPwr[akga_bijhfbaefijjidaaggggebcegcbeebejiheb_`cfkddrf^[TJqsysgdeb]bso`jsiklcfgYZSPUWUUYVYZWTTTTXY]_\WTUTRUX\YJAIRPLRSKEFLRSPQROWNN\lsx|xwvvtnlov~wd^jj^Zp`Wutdgg\X_\V_imolkg^]elhihhhjjjiihhggggj[Wvk`liijggeegghddf_ROOT\hoknnimi]pbhsopprlllorpnqqoqrqsuttrqqsxo\߀8anggdehlngfw|{|~|}İfo|x{~}|xvohhkh`cnin{joyzyunnrmmqpqsvvtrsqu}sqmki]a[Z[[Y\beeehkg`][\irlc]]kxtmfdfhlrxwsrtwxtsutpmqtsrswuwwvuvt{yutwxwxwvuuvxxwxj[[o~~wwzzxyxwx|wtvyyxvwtuvuvxvwwttwtsw{olqhS<3=CF`q_LCG@DgnRCJLMPRQRQRNKSYYWd_\e}qsɾļ‚efRPwqZalhcadiifeaafeggffc`agghhecdgiihggfdbghigddgjidevhi`aSQngqwpghhc_q}kbprmoj_ei\YWWWYXXWWWWUTUUTUY^`^YXZXVXWYXK@HSTPSSKEHMQRTRPQTNNYhsxx~{xxwuumosx~l`gg]Zo^Usrafe\Z`[T_ikfkql_Ybkkihhhgijihhhhgggmb\tõæg`lihjfgeghffccgaVRQT[flhikiok]mdktqrrsqspmqolnnorqlloqvsrqswnZր4\hdc_afh_S\w|}~wz~öǔafy||}}y{uxi[]ceddfzsuwwxnouronruwvuuuuuwz}vqu^`_XXZWZ^bddefhhdb_^grkb]^gvxocdfhiqwyruvwwvtqlnqrpmpuuqs{{xvw{ztsvxvvxyyxxwustj[Xhy{vstwxvz|yxvstxzwruvsrtuuxutsx|wvzg]ZYO?757=AIMJEBFB?XhTAFHINPQURWUQRUW\\Zdntxts˿Ľ‚fgSQwl}]dmidbehfefeabefedecabghjiebdgihgeddeehigb^_elidejbhZ^LRr\]oumehga`|wgfvqkom_`e[PTVVUWWVVVUUVYZZW[ad`\\_YVXVUSE8EPTUVQIINPORVRMPNQX`hryzy|vvvtypqsqz}rhhc\[pzZQpqbfg^]bZS_iiknpj^[bhihhifccghhhhghggkbZp²÷Ĭk`liijggecedfceidXTRUZejfeiipl]kdirlnmmoromopmmstsqrqprurrqswn[΀5[f``\_dbWO_{z{xtuz|ĿƔhp}~~z~zv~kZ\]dffxnqoqwonuutrrvvrqtvtyrt{z|}~``ga]UV[^`adhhfikib_]blob^`bo{tjedfjpustuvtttsrsrrsttuuvqrwzywrvwvuxwsqzz{yxvtsxp`Xev{wuuxxwz|xvyzvtwxvvwurssuzyvsuvsvdPPPIA;>;12BIIJCCDEDMWM@CDFJMPWZ\\[[XY_^alihrslʾĽfhTQvk|^emgbabfgfie``fiihhebbghihc`addgheceghbdfdaafkidg~caeYcJZveOUntjafd^ctcfvmksqcdh^SRSVWUVXXXZ[^^^^\_dfa[Z\\W[[ZVHCC@AEFKTZZYZ_]ZZZ\numghpfiUQvnz\dkeaa`figea_bcfgdb`bgjiihebbdicbfd^`hjigdcdgkfallee^^unOvd[KLirh^dcf{k]nxgflgZ`dUJWbcdgfaabefd^\]Z\`dc_^_\W[ZWWK<>OVSUSLIOLNQTTROMOZabmzzyz~xuxxyvuxz~tga_\n[Rqp_ccb]`ZS\fkonkd^agiijjihijigggghhiib[XpŨi`lihjfgeefefcdhbVRRT[fkhgjhnj\l|cjplrqoqsqnoqpnqmmnopqnsqqqsxp]̶߀:clfmpjacjfgwy~woottw~~~zwz{kgejrpjxumprtvtwvqpturqtttylajl`[[][Y_ijfgmljife^Ziqp_[_bluqebhjimrttuxxwvwurqrrprvrsutruxvxwwxwst{wsuzysu|wtdV`v~yutx{wuxz{yyyuqsyuwvvywuvzwvrpyzlOV\UE??=A<416=>=ECFA@CB@EC?@CDISW]\Z]_^_\^nkZdssȿҿgiVRvju]fnheedgdcec`bdikgc__chgffebbchgfffeeejhda`chkc_o~igebczsV~a[MKgrh^eemi^w~zoqpb`cZR_jkd^_ccbcb^Z[_]^aeea^_]Y]YV[R@@LRSUOHIMMRSMOSRNLYfcfptzvyysuzzts{{z|j`_]oy[Rqrcgh_X]ZT]iqnome]^dfjkjiiie_fgghhiiii[TpìĦg`lihjggedeccbelgROOT\hokmmilh]o~ekojonknollpnlpnpooqqqsusrqswn[}һހ@ivtyytoszj_ru}ywrls~|wy|}ϼس}}yt~a_mlln}|xqklprurrrrqonnqsoos|`hljd\X]Z[^ejebgknke_\^dmm_[_bnvrffgfghrttussvxussttsqqrusqoptxzuuwyxvuvwwwwwxxxzveV_v}wwvxzzwwx{|wv{zuwyvtwyxwxyttwsuymYUWVTMAA=>;548>BA>BDBCEBC??CC@DMVUVZaebZX\_VRkssȻºֽfkUMsjs_dkgdb_befedddbfgeb``cegcaeebeihhgdacfhie`_agnb_p{gadlvkV|eXLKasg]_hzlqvushca]\^^XYcfc`^_feeb][\[[`dff_[]]^\YVYUB=HPPTRHIKHNSOLQVSKR_ehotvzztqquwvttx}~od]YoyZRrp`ef_^_YS_ljkpodZZcljiikkhgikihfffhii[Vs«±ƭĥl_jfefdfdbhhgdfh^PPQRZinggnllh_o}bktmpqpmoonpsropnqpjkrrrotqqxm^݀Cp}u{{st{j^o~}}~}zzols{wtw{yxy̸޻xtzzhZ_oron~q`krpikmpsqptutpnnnmnot~l]kogb`\Z\Y^cbegbjmkiheabord]^^iurhfefkmtyrptrpswrqrwvsruuttuuvwxzxwwvutuuxywvwvstviX\q{xxvuwxwxy{zutxyvw|ywvwwxyxrtwps|q[PTXWVNG==<86:>?CB?BGVVUUZ_XM^h[]dlnrʿø~ciXRtir\djea`dghhda__defgfa_bkjdab_]bliffecdfmkd_^_bggdvuTRhzyzmXNVhvokq{~~vjdeech`]_]^aaYYab]]]a]^ab][ZXZ^acd_\^^\Z[YZTA;GQSSMFLJKPSQPRTSLQ_ehntz{{zywuupsvvuvz~|sh]Wn{\Tsp_cd`]^YT^kkgfki[Yenlifffehkjigfffghi^Zui_jfegdfdafeecgkbSPSWZbiilkhni[m}bkumpqppqpmmppntppronoprotqqxm^݀Bs~pt~tsyi\n|}{y{xonw{|}z⿰urt^WUV_ie_jglvzkuyoiloswvuwxtoostrsux~}[_lpgda][\\\^cfdaeijklhbalrf\][bpric_cjlqorutsuupostppstrttvwwwvuvuvy{xutxzxsruursxn\[p}{wtstvuvvtwyxvuvxvxxwwxyyxtstos{s_PW]Y\WND?>:58=9:?>?CA=@AAAA@@BELOSUSRW^ik_a`bhn˼þdjYRuir\fmd_`fhihd`^]edcee`^ahihhga^cihijjfdciidbccbdigwcFTtzrii{~trz{gd_\^_]]_d]]_^^_^WV_a]\[^\\`a^\[X__^]]YX[\]\[USTI9GSRTRHGNPOMPRTVSLQ^egmtux|}}ytqwvvuvwxxzym\Vm}[Ssp_dea\]ZU]ilkkngYZgniijllihhiihfffggk`\vţf`kgegdfecfdcbekdUNQY\`gknjfmiYm~bltmoqomopnnopnspnpspoqrotpqxm^݀Ar|nr}~vpte\rzvxurj`gw{zx{yssYR_`S[c]e{skZqđplprtwusssssvxxuwyz}^]cjmlh`[^^\Y^hfab`gkljfbcgog^aaervmb\afjnsssrqsvvqstsrrqqqqqrrsstyvuwyxvuwxxvuutrvyo\[p~|utsuwxwuzwwxwzzytxzwuvvuyyqpsuvsbW^_YYVRPDA?43:<>A@?BBACCC@<;?CCAMPMPY^___ab]a\d̹տgmVOtku]jpe`cbeggecaaeffhgb`cegfec`ahffffc`aecgheddiplShjHbzwxuoix~li|jd__a`\]cd^^b`__]YV[]\\[]`^__[[\[Z]^`a[VW]\ZXRTUI;GRRSSJGNQNNSTRTRMP]dfksxz{{|{xtwtqsx{ywz~q[Wo}WPpqagia[[ZU\hllqpf^]chhhiiihgfhhggffffn`YsǤg`kgfgegeegeebcgaUKLW_dikjkglg\n~blulopolnonmmmllsqklstprotqqxm^݀Dq}ssspkrse^swx{~umtŪ}r`RVebM^gcqsdss^Бzmlruwyvtqpruxvutxwz~fTaekmljb]_`Y[cghf_^flmlgcdjrl^[Z]jzqb^cfjpsqtwuqqsprsrrqqpttsstvxyvtux{yvtxvvxwuuvxwm[Xl{{vuuvz|{yvsstuvwvvyzvstvwxxqptuusd^^\[XRTXJGG;05=C@AA?BFEAB@;;?A?AEL[rwhZcd^\^_VeϻʺѾglVNtox]ipe`c`cffeccccffdb_`cghgc`_ciihgfcadhiiecghhjO7`uUqzxxukduf_{sb_\[YX[``[]bb``^ZUZ]^_\Z`]]\ZZ\[`a_^^ZX\W[[XSTUF~xupprtuvvvvtzjVY`]_eimprof]]_\]hpmida^agllienri\W_hpul`_ackpstuurrvsssssstvtrruvwxwwsqruxywtuuuuuutuz}q[Yl}{vuvtsyyyywuy{wvxwtuyzwuxrlmqv|q^]bYV[ZTV[YSVRBEKNLMRPGEHIKKFFN[_ZUWZ^fc]OOžҹy`nVIro^ckgc``gifefdbbdfeca``mjdig\^cjijhfjdT;41781++ &^t`~cmrirxZ{X_a\`[\pve\`a]`a_XTUWWY[\Z][YZZWUTX]_[VTUUTSWWTN?6@SWQMHGJPOLNQQRSLKWcbcnqqtvwzzssqrqpuywy|zhq{[Ssrbik\]bZRYckmkkf\Y`fhghkjgfgbehhfddeg^Xpµúƞh`lhgheecgfdgece`WONU\diikdbniZncktknnlmoqollmomlkossqqrqsmpwka܀>ʼ|xssvxxvzy{bZ_`[[dhmprqjb\^ZYdnledeb_cjmlemsm`X]fovpb]\bnnqsuvrqutuvtrqqpsqrrruwuvtwwstwurvxvvxvrttxrb\l~xwxzztrvzxyxvxzxzxvvwxwvvxtlilt|w`[c]UZ^WR]]PRWJFINLKSTGEFHKIBERZ^\UX\^_`_KLøµӽsZmR>ji\_kcVRYdc`db^aacdca^^_dgchfWW^daflgWC352495)##&5pt_qYbigtsVyU^c_^Y]ptaZ^]Y^^ZVXWWYWV[][XZ][XXWZ`d`UONUTRVVTO@7@TXPKGHIOONOOPSOIHS`acmpquxy}~wsqokjqwvvv~~op~x\Vvo]dh\Z^XU^flllnh\V\bifgjjeegdfhhfeeff]Xoû¤Šj`lhgheechfdgebd_VNNU\chhigfmfZobktostpjmonoppoonponnrurqsmqwk`က>u¾ywxyy~acc^Y[dimoqqkd__\[dppigea^`fkmflspcX[emwse[[blnssqrttuttpmmrtstttsrvyvuttvvxxtsvwvvxwuzuvueZe{zywuuwy{yxx{}vsxzxwyzwuuxzulfiqxxe^c_ZZ\YX\\TSTRIHMNJRUJBCINJBGUX][VX__[_cNSúԻ|_cNSw^drdIB]igdkgahghjihghibnknfFFfrsmZA2*&5<=5(&.7/9ldfxR\jjqqV|Y^b_^X]mrd]`_\_^]\ZRLIGHOXWUWZYWXXY_fcYRTUTQUVTO@7?RXOIFIINOOPNOTNIGQ]acmqrwzz~zwurmkrywrry}qp{vXQqm\cfa]^XT^cghijg][bidcehihhifggggffgg^XpǪȟe`lhgheecdhfdbeg]ROOQU_higihldZp|`hslpqnonmmoqolpnprnorpqqsnqxj_ހBbzxxw~aff]Z_eimnooke__]\cnqlle^]_bgkkmpnbVZghuwi_^adpsroqtuvqqqpswvrtturruyvtvutxyvtxuuvwvxzxvzxfTa~|zzz{zxuxwx|~wswwvw{zvvyyxtkehqvyh`cd]YYY[[\]WQPMFKOKOSMEELRLFO^_`[SS[_[`cOUĹԽz\`KWz`cphK?`khfmgajijjifefhfgcoiEEhtcK5(#*4;70,/488 +bjewScvspq[e_^^^[]inf]_`_^\_bZQICCINUWVUWVVW_\]`_WVYTSPTUTOA7=PVOHFJIMOOQONSSNKQ\_aiprwyx{}xusrnlpursu{|tvxZQn}n^ccZ[`YS[fnjhhc\Y`eehjihgd`ggffggggi_Zq¹µƥši`lhgheecbedccfi_RQPMQ`jijeeogWmfovklmlnmmmlklmnnonnopppptosxj^ F_}xx}bikc[^eimmnnlh_]\]_gnmnf_]^^cilkpqdUXdjtvl`^`brqprtsrvwuqopssrrrssqsvutzxuvvtuyutwwvx}vtuylZa{xvzyvutvvyzxyzwyxxywuw{vvsldhqw{k_dh_WX]\YZ\YSQPIJOLMPOJHLNHGUc[[ZVSZ`]_aV_þտnSfJG{xtwJ5&-513AF6,3:6*"+iod~tWj~wqm\|i`]^`^agid[\^^[X]^WVUPQVWSWWTVYYY[YZ^]ZXZTRPSTSOB69KTNIFJINNNRROPTPKQ[`cjru{|z}|ronnklnmrvz{uzy^Tp~o^ba_^`YS[cijgfd\\bgcghffjhdhfddfggfi`Zr¤ği`lhgheeccbafedgbWRONUdmiheemgZp}cmthijjnmmnlkmpnsojlmntpptpsxj^ѿကDgz¼}|eioh^_dimnopolfabc`dmqnje_\\bgjiosgUUamruobY]dqqprtrpsqpqrrppptsuvttvwvwuswzytwvwxyyz{zuuzt`\l|uvywuutwxywvz|x|zyxwvvxvwwodepu{pa^ebYU]]XVZZWUQMKLMMOONKMMFHVaWW[ZVY_^]ddi¿¼ӷnXgLR͉ɫlG,13499;B;645/#"4mw[xp]p}yxm`q|zl`^aabhhdd^^]^]Y]ZUVWUUXVRVUSVZZY]]`a`ZTQTRORTSPB56FPOJFIJNMLRUQMQNJNZ`dkrv~~yrqtpmoqpswzwzwZRqo_df^Z\WV_finlmi`\bgfhgdehihhfccegfeh_Yqµƙ``lhgheeccbaedcfaWPOT[djiffdjf_s~cltlnpoqonnonnmopmkprpooptptxj]ʲр@ks»~řgekgagdimnprrpgabc]^hplnkaZ[bfmjmpdSTcknvvfWYdorqoqrqpnoqsqoqusqtwtrtvwwxuswytswyyy|{xpsywa[l{y{{wwxuyzxwxxwyxxxyzzxvwz|rbblrywdW`f]RU]YW^]UVPQMJLNNNQORQKMX__[\ZQR[^_daUpǿļһlUhOR˾Ƿ_icddonknrledeaba`__bec`agX>2(72-3>;5971(  "7q|a~sgw~}tiqw|s``cbdmja^\[XYZVW[WX[[\\[YZWUY[ZXZ\_`a_[XTRORSSPB54COOKGIJOMKRWRKTPKNW]`fns{~}|squqmotprw}{|xZSslZ`d]]`YS[dkdfjg]Y^eghhggggfhebbdffef]Xo´»Ûf`lhgheecbfdcaeg]SLOZ]_ejhb`lj^rdktmpokkmnnnoonmknpnsvroptqtyi]βԀ=`sÖhgmbadgdinnqtphfb]]agljomd`^]_ikptiVTblnsreVWbloppstqoqrrrqrsurvtrtustvsvyvtvuvwz|socD:Nr|xhVj{~{vxxuyyxwuv{{u{wttxzzyyuun`bqxuwiZ`h`TU\_\[\[ZVPJIJMOPROONGRdbXV^[STXaaYa^YؽkUcSTķθŹlsrhktuvz|vmmsoooooooorkuuT4.04/'4B<53*("""5n|xfx~mw}rvuja__^`v~h_`]\[Z^^WY\XTX[YW[YYZZXWY\^^]]^[WRTTSPRUJ:4CTOEGJIHOTPNOMLOIKZ]X[owx{{strsrloqqqvy|}s\Vo}j\_f][^YU]cfefjdXYceejkeeigafffeedddf_ZrŽěe_jfdebcbbcbdcdg`TMLS\fjfgfgjgZsz`lrommkjorpoppomlorpopooopmuwhcˬۀ>gtyýfhngcdghikoqpojc__^\bkmolgb\Y]fhosiWUchlqocXZbnttooqrrquupnqsssussutrttqrvvwwuvvbI60+.>f{}mXdx|wtwwwywtv{yvvxzywwwxzzwwyrccmpxzl[_hcXV^`\[]^]YQJKORTUTPJKN[g`SS^^UTV^a]^`Z_yü¶ٸjVfUSɹyei_^ifgjmlcYUWVVUUTTSSX]b\G6,'.-&090(# 6q{p{}uepvzmfc]Z`g`_}g`[\__[\\WZ^[VWZZ[YSSYZVX^[]]\]^[WQRRURQRJ;3@TQHHJLKMNKOSQIMIKWYV[qxux|y}~{xuplprttwwy~wYTm|i\^dYW[WV`hmkgki]Zcjkiijjigfffeeeeddf^YqºǾ¾Ğj`kfeecdbcdcdcdf_SMMS[eifgfhki]w~clqnmppnlmppmkmqrqqqoorqqqntvf`ǫڀErxoҾĻxsyehljfefjhipoloohddb^ckqqolh^Z^gkpriVR^hmqmbY[blpojmrsqmrtrqsspssrstrqtqprstvwtuJ,)#'456ZwzlY]t|yy}zzxvuwxvtwxzzyxwxyzuw|tfdlov{qbbjfZSZ^ZW[^\^ULJNPRRVSIIUag_MO^aYTT[]d`\`[]mɾþϹiTaPMyyktjhoijnnifgkmiihhggggckl`K4*-.2-..$ !7wzw}n]jnrj^\ZZ]l`bd\^]^\Y[]YW\\YYZYXZZYWXY[[Z\]\]^\XQPPTSPQJ<117MTOJJKIHIKNOOSOHL[a][``_WXWBozff\tsdfer|޶ɱpazty~}ȿv}whpo{s_e_bghkfefccikimqqojfcbbcgjopg]\[imop`T\`]`c]UXckpqoonlltqqtsopusoqsqqrqrttsqmnv}f07bzxm_]p}|zytuxrrwxwvuwxyzzzzz|wuxtjfmsu{wg_ekjUR\]\_^\^``][[][TS`aX[`]OT__XOP_d_Zeup^|λڷgRaNK}i]xlWaqspqvwqnpmmmnnoookwsZA5346&!7r|b~zh_eZ`j]aa`e^\]rS^a][ZZ^^XVWYURUYUZ\\[[]][]^^[[[WRPSGBJPQMA;35CONHGKKJIEITPLIHGINOORKNORciQ=EV_ihftrotx~zi^p|k_aeYW[WT]cghjhbZUYcfijgefijfffeedddgYYtŸŹľěe_jfdebcbbcbdcdg`TMLRZcfbfdfheXqz_inljllpjglppnmomopnoqrrrrntue_|߀Eiyadzbkuպݼ{cvz}||~~űzgoozdcfe`afihheccehlqqqomkhfffglrqng_\bkqq`R\bY]d\W\amopqpoqurutqorrpvspnopqrnstrqssp~k;/Wx~rf_t~{}zuvuutuvyzvtvyxwwxzywxynip{xzzna`ee_QZd\YY[X`f[W^d\SQYa`]][WX`]XWQ_aXYhurax»Ĺ׭hOaKKyiWljakturv|yldegdcfhcafefgVB6.2<)!7msb{on^f]]d^gb][`VcmYbb_ZWX\`b[ZWTUVUTYY\][Y\a_]\\[[YSUSD@JRSOD;58>HLIJHGHKMMLPNMMICADF=AAADOiT2/<;IVTNS[\`mwk^rk\b`[[]UR^ehihgbZZ_bhhjhdefcbdffecdek^[p|²öÜ`ahcehcddfdbcfea_PMMQXbgcbdfjfZw{akplhklkkklllmonmmppoopnrppotb`倀Dmq[}wdu}ɴѲtZkoy}}xxz|ɧwqqnq\igffebcfjeaacflrqonnlhgjfgkmllh`ZajooaS[\V\cYT]emprqonpqommoqrststqmnstsrvvssvsmwmA)Qt}pcV\agt|wmwvuuuvwyxutvwxyz{|zzxojqvuwyreaggXQ]aYZ_ZV[b_\`c^TPXab^\`\[a_YXQYb^[hxu`tķѵmRbLMdQgcW_ipllpmd_a_dbadb_dihhR;4.69' 3gq`vwn`f]]f`gb][\YmiZb^][Z[\]^YXXXUTUUXZ\]\ZZ\a^\ZXZYSOMA@JQQKE=649FOLNJFEGKNOQRROJFGJI?;:<;8DN3!*'.48349;EXda^r|g]c_^]^UR\ceijjdYV]bbdhhdeebbdffdcdef[ZsͳÛ`ahcehcddcdbadge`QNNRYchdeccig\w{alpljmmpmlmoppnmomjjnponrppotb`Iifhue|z°ԸpTceu{{vw|{rlc`fa`bb`elhdbbceinkoqoookedinnmnkb[bknodW\ZU[`WS]dinqonprqopollnppptsmnuxtsusoqvwr|rH3N_]UOSNMOVdqwxwvuuuvvxvuwzyxwsx{{zqknrtvzyiahl\TW[ZZa[Y\_^[^d_VQW`da]\YW]^ZYSUb_Xauxcr¹ƸϳnWgRNwspr{~|zuvxtpsyxw{qccRA4)67%.`n_nnah[\icga^\Y^}c[a_]ZXWY\_YUX[WTVVZ\]]\\[Y\Z\\\\ZSNTPLMORPB>935DONKIHHJLLLLPQKEDGJKEA=@@>GL7#'-+.20/233;ISSTmo[]dZZ\UR^fhehkeXV_g`adcbfjhbdffeccddXYr¯ŲŴ´Ûaahcehcddced`bhe]ROOSZdieecchg\xzampmlookkllmnpqlmoqrpnnnrppotb`ۀJgj̭wg|z~޽ֺrUcdw}xwsѮwut^hggd`_bhmgdbccdgjnpolnqmgeinonole^cknoi]^_WY^VU\`hnqnmrtsmqsqkhhjpuupotvtrrqppsuuvzW$ %3:FLJDFMNKOPKPevxxwvuuuuxxvxzyvvv{}}{umjrvv|~n_djf\NW_UW^^\]^[]e]WUZbea\^\W[\YWPQ_^V[owfqƻɼѯkWfRLvm]{ohipnnoi_WUZPWaXMS`G-5AB4%:;&  ,\m`hochZ[mgg`_]Xc`\^^]ZWUW[^]UU[XUVV\__]\]]\[XWWWYXSQWSQOPSO?=;76@KNIIJKLKJINRQKFHJKJHDCEEGPF.$-.*-1.26536>CMZr}l[X\\[]UQ\cffjleXT]eegiifhifceffdccdhYWq~²Ÿ¿ęaahbehdddbccabgf^RNORYdidcdejdYwx`lollongjlkjloplkmpqnlmnrppotb`܀Dkœ|j{~ӰϲuWefx{ztq||ƛxĦ~b^gdhga^aefjfbaabfjllouumhhhgikklmj`biloj`_`XZ\UU^bkprlkorqqklplbepsuusqqrtrrsvtpqu{\)6CBIHIKKJKNNLQXTOYgxwxwvuutwxwuxxxzxyxwyxsnswwzn\[^caQQZRO_]Y\cb_c_ZX[bca_b`Y[^\ZTNZ^\]ksgoǼȻͲkU_MHox]AU^TPPQONNNKKMEF^b;%7J1";D4%7>' .[mbf}qcgZ]qkg`a^Zfy_][Z[\[XWXZ_VVZYTVX[_b^\\^^`]\YXXWQMSROMNTQ@;::7;GOOLIFFHKNRSPKKLLJHDCIONNM<&'/**0..23115=EO\pwgXZa\[]TQ\cegijdXV]cefigeefccegfdbccjZXq~Ębahcehcddgccdbcb[PMNQXcgcddeidYxv_kmjknlnonllnnllnnlmoolnrppotb`Fpڱ~l{âٻtVcduu|ppy~{Ыz̽p_ee`aa_\^chjfcbbadgmmprojjnjghkmool`^djmj`_[WZ[RTahjnoljmoongfjjdfnrtvuqnpstrrttrsv{lN;DRPHFFHLONKHOOQVUNSdvwwxwvutrwvuxyx{{yvvxwpkqvux{p]TUU`ZHKWY]]\^db_a`\Z_cc`_``WW]^\VSZ^^]itlmƺĸȴmXaQLltdKXZQRWTTSTTTUVMRdX(*7%!6=3+44#!1[kbewtbe\_uph_b_\is_\[Z\][WVX[^YXZXST[X]aa][[\^]]\Z[WPLSSPJLTSD<9:68DNRNIDCFLQRQONOQNKJJNWWZ^U,")*&05../.-039BKSiwiZ^iYY[TQ]egefgaXW^cbcddadgedfggdbbchYXuȻŽ÷ėbahbehdddf`adbcc]PMMQWbgcgdbge\xv`klhjmjmppmkmnnlnopponlnrppotb`ကH}ɚ}iy鿫sUaaqvroyųջx~ǯidjdeedc__eligeedbbdgmqpoomjkhhlptrkb[aini`aZVYXPSageillkjkkddfgihfensvsooqsuurqsslcUMFJMFADIMPPNMMNJRRUXML`tuwwxwvuqwvtyzwxywwz{ysmptvwzudSNOafJE]d\\]^_^_c_[]ejd]ZbbWT[[XRW[^][gunmƼ¾źʵn[dUOkrrU]dbbb]]]\[YWUTUWE%$% "7>201( !2[g_csxac^axsh^c_]ko^Z]]^^ZVUY^]\YYZTRZ[]`a_[ZZ\YWVVXXSOOMLKORJC=;;68AIONKHEFJMQQRUWVTTQT\`Y_dK(','(11021/2655=ESmuf^a_\[]TP[bdgggaWU[_egjjgggcdfgfdbbbgVWuíźĖbahcehcdd`[_bagiaQNNRXchcgdbfe[ywbmlhjmigmplikopnkknnkkmnrppotb`ހ@جzetϯtU``pzn|ylx|٨Į|ͱ{nceh`bfhfbbeilhdcbacfkjjmpqnklhffktskd[`kojbc^WWUOS^adglnkfddbaabcdhkirvqnqtrtyyuuqZ>6AHGGFFFKPTUTQNKOUOP[QJ\stvxxxwvvzurwyuuzvvxxuqnpswx{{lWGPgqXK_d\YWY]adfd\[chd][bcWU]_\VTZ^^[gshoȿҺpY^PNkzu[]_\[\^]YUTUVVVYXN9#&( :D42/$2Zd]aoy`a_czth^c`]mm\X`Z]`^YVX[]^YX]WQW`__a_[Z[^\\[Z\YSJLMMLOTM@=>=9:@BMMLJGFIKJKPVVSSURV_d^hc6%&($*-(241.2965=GMgte[abZY[SQ\cfdefaVT\aeegfcefdefggdbabhVTrŖcahcehcddfcec^cdWROOSYdieeeehcYxxdnmikniknokknomnonllnnmnrppotb`ڀ;âygwỘsR]cumuymt~~Ɣźͳ{{kklf_cefc__`chmlgcb__bjkgfnrrspnkhghlnk^]gkjfa[UWZUW^_aaehfcbdaabdfghjpnorspoqsslu~a?@ACECA@BEMPRRPMMOOPORRFFYuwrr{zuwwxxuty|yxyusw|wmjz{u{p[K\plTUgb_YY[\affda_bfa]`fbXY_]ZVMU`^YcommƸýѲlWdQKqvtV\`]^XZZYYYXXXPUUPN<&+$)-?E3.7% 0clY`s~~y_e[Zyk^]aftk[b_\[[[ZZZYZ^ZWZXSS_]]\Y\^Z\^_\Y[YRMOPMJOQME@>?<7:AIJLKHFGJOQQSYXTSU^idelN)!#%')+/2110//26:EIavhY_`[Z]XT\bgjgg`TU`fgfefgfecffffeca_bVXsŻļǚd`jecdadced`aacf^ONMPYbeciccgfZv`ipmjjflomhkpngmoljorqqpnpppt^bကE̴wcr{ѲuU`dr}mqtgn||ݲŠ˃ɿ¹}fcccah_fe^\_gojjfdfd``gkjjpsqrnnjghnomo`^ipne[[XURRZabcaacc`^_baabdgiknoppqqpoqrrriQBFBCDCA@BDKPTRPNJECPNHICJcvwvwwtqsvtuuvy|{tyzuruurmquvzv_]mm^S_j_[^^[]acdbeccdc``c_UV]]\YPT_b]eqsmÿϤbQcRLqvtV\`\^XVVVUUUTTUSSORS@0-(*;?668) "-_o]\n}y`d\Yxm_\bjuiZa^\[[[ZZZYW]\ZZVRVZZ]^[[^_]\[XVYYRRPQPNPQNFA?@=954?A4?ewquzrpuxxxvrr{}wwz{yxyvqtvrr|~tjqlTK^pm^\a^Y\cedb`_cdb_`gfWRXYZYRR[a\aknnȸѬiUcOIrxsT]`Z[WWWVVUVUUUOPTSD=<>BFHIHIJJHILLNUY[`d]^th4!#$&'*-0110.-.035@WkcZ`a\WXTT_bcdejdVT^ceghhfffgbcdefedceYYtóķ½c`jeccadcaa`cbcbXPNLOXbfdcbeidWvcinkjmkkmmlkmmlnmlljmonnqsmmwa_؀J}{qy}|ȿ~jqlu}~on}opr|}}}ߵürwxмج}jgfff`^Y`a\\`dfilkhhgcaa_dkia`elilnlgiomd]bjjbZZWNMX^^a`_]ZYZ^`aehikmlikjlopnnplrm[KB=:>=<=ADHJKLMPRRMH94402Uzyqsvqryzzttuusuuvwutw{|yqvtrzztzo[T[hmc^]^\\aedcbcedbbbdfWQY\^^UV^a^dnnhְmZfQIrzrT]aXZWXXXWWVVVTOX[D!>^H5=B32;'"%!5al`\brrb~gd\Zxzuhal}zqj_X^\\[[Z[ZZYY[\YXWXYYZZY[]^]`\[ZZ]\XSQPOOTTNFA<:;<@DBGJJIJIFIMNPVYZ_og]aO$! "$$&(+.110/--/15;OfdZ`c]XYUV`deggicVT\_dfhhfdfgcdefedcbfYYsƺüÔb`jebdadcabaccbbXPNLNWbfdbaehdWvagmklpnkkkjklljlmmmomlnpnppou_aրM}vpwyz辂qxozwu|jlr~~}uu~̴بuhgdbe`][_^[]abbhmlgggdc_\dqqieedfhhhklmgebfmkb]XRNS\]\_]_][Z^becefdeijhjhjopmmpnscLD?;=><:<@EIJKJKNQQPPLE:5B]puwutspswwtrtwusssxwtsuyyvtuwww|}tfc__]dngZZ^a]_dfcfhe``a]dhYQZ\\]WX^a\bjimѨhYiSJr~rS^`XXVVVVVUUTTRQcZ-:[\NF?007(;dh^_akra{mc[[x~ulgozkd]Y][[[[[ZZYYZ\ZWVXXXTYYXY\^ab\Y\`b^VKNQQQTQIEB???@@AAHMJGHHFFJKMU[ahsom]6 "#$&)+010/..0157F^aZ_d^YYTS_fihee_SS]abgjhb_aegfdcbcddfYXr~õµ»Ĕa`jeccadcccacbbcYQOLNWbfegcceaVuakpjhmnljjllkklqnhhmompokmnnr\`̀H}rnt}{zؽ辅v|qv~{npw~Ǻθ̜ndf`]a]YXYZ[]Z\bfllijic^_\cnpld\_cecdillmh`cnm_USMPZ_^]\aa_\\`cdfihdeikihijmnnnornW@==:;A=:;?DGHFILLMPQQHKNPMIO]jsupqsuusuywtvxvrwzwuuvwytuxxyxqXTZiidgc[\_a^]`daceb`cc]cj[QY[YZURZ`]aikqΤfYhRIrrS]aVWVUUUTTSSSQNXI'!+>LRQA05=) >eg___gm_vrcY\yunorxg_][][\[[[ZZZYZYYZZZWRUYYXZYZ^aZUWZ]]XPQSRQRRMDCAABA?=BKNHBEHIJLLMU^hrpqvT!!!"$')/010//0255?T\Y^`\XYSR\ejccf`TS]bbccdeddcedcbcdeffXWp}óø±³a`jecdadcfeababd[QOLMVafed__cbYyblqjgjiijlllmoojjkmmklommomls]^ȀHqmt~~ѯ辆yrp}yjs{}ϽϿ̿gae`[_ZTVXVTUWZ_eihfjlfa_\ajooh\^bdcachmig``ii`ZRKOY[^a___]\_dgfdhhddhhefjlklppmtgM:;>95D@;;>CEEAJMJJOPNSMORNKMM[oupqttvsstsopuwvwwvvxxwvqnt}zstiWWptbZY[]\\__`fgaadefd]]gYPY\Z[\U[da`hlbɪjYfOHsqR^`VWVUUUUTTSSNV]K2#!.A;/9A%#>dg`^\ef[oubX]{upstwd]^\]Z\[[[ZZZYZYZ[XUUUVWWY\ZX[]YYZZ[\ZPOQRONNLID>:;>@@EMOE>BILILLLQW^fksn? !!#&(/010001334;MXY^\WVZVR[cghhi`QP]ecegfdbcd_acefffffXVp{·a`jecdadcgfaa`bd\QOLMVaffe``eeZv^gomklhgopjgkljlghllmnlmqsmmxb_ȿ̀Jnrwylt}~}qt|Ȓ|¾|feb_[XWUKPVWUSSTcehknnjg^\_hprlcZ^ccadhgbgecgg_YQFN][Y^ab_`a`cffffeddfikgpslgimmrY@899:=?>:99=AABKLFCDFGKLLLQXYUjwZ( #&)+.--/231/26=HRX[]WW]WRY`ehde`TRZ_^gkfacff_addcdfi`RXwƼd`f`bfbeecdbbaad\PONPXac`fgbdfZxcnsnhjmjkiilmklifinlloqnssnqx`bЀNx~ñt{xxzoqums{{яy~̾jjhd_ZWTSPMKNTWURY^ejkjhha^`gmpmf__acbcefghb^ehaZKEP`^Z]^_^`cdefdcdedcejoqsqkfgkneQ=7889<:;:<@DCDLEADGKR[WOLQPJKTN^pvrqrotssssuwywruxsruuxrqvyywqlddg`ceRW\]]`_\\`bca`bcca^WR[_Y\aXX^]dnhf;Ľ˭o^gPKurTZ]VUWUUUUUUUU[LX_>(=<27>)Dfa[cbac[fm^`yzz~uuwg]]Z[\Z]][[[YWYVUWVWURPWWTUUUX[YYYY\[VTQQRQRPKEC?;;?@@CLNGCCGMHJLNRXYYgbD! "%)+.--02320407GSV[b\Z\VQ[chb]aaTPZehfgjiecedcccddddcTYwȾedibce`aaccbcaad\RQOPX`a^dfaceYw`ipnkkkkjjjkmmnlmnljmnjinmiksZ]ǀOs{{Ž‹sts{}|wlrwsw~~В|Ǹ}ımnje_ZUSQPNLLNPRSSYbhkkkkga_chmnjb_`bbceeffa_gkaUJFP]\Z^a^]`dfhgdhgd_\_hpklnomgc`SF:7878;9:==?BDABDEEHMSTTMLPPIIONWlxsnontsrrsssrutvyxtu|xrpswzulrtm^ZhjNR]_\``\[_aa^]]^^^\WS\^W[]V]ia]jkfϸüүp^hQLvqSZ]WVYTTTTTTTTTMW[? $&#-?=16<) 4U]Z^`ec\`om^dy}z|xsrd\\[[\[\\ZZ[ZYYYWWZYVUQUWVVVXYXXZZY[\YQOQSRPLFCB?<=@A?AJMHA?FPOLJJNS\dfM/!$')../03332657@LV[]ZWXRPY`deaecVP[efdgjgaafheccddb_fVYvĹǿdagabfbddbcbcaac[NMMPXadacc_bcWw|^gnmklklkjhhhjjlkkkkmmjknnjns[_ƿ̀Lmzyx~˹̕rnqyyxvzzspy}pݺΏ҃rmnke_YVTSQSSOJINSTV[bhllkfa`dimlga````dgfgf_[bibVBFT`_\]^`^`cehheec`[Y]goolhfb^VPD>9:9779<<@B>AB>;?B?>@>@?@BGIJIFIJFGJJGAWszssywuutsrsuv{wvrrx|vrnpttrqppgc]Yit_R]`^`^[]Z\]]_a_]_dbY\^VWWZX]^dsih˾֯o[eOKsrT[^XWYUUTTSSSRVUNNUI1%+$1HC369(7ek[^c_`ZW^m}lbmz{}~ojbYUTW][ZZYYYZZXUVXYXVQPPRUTSRPTVZZX[^]WTTUTSPKA?=:<@CBDJMKIGKSWSRQQPW_W@3." "&',.013344722:FQ[bZVYUT\ab`ci`PO[bcffaadgebcdcbabceUWt~ºü`dibce`ba`bbcaabZONMOW`b_cb_dbXy{bmokillimmkmjhliklkkllmrrrqtw_eԀFˢ»|ƿ÷xnokd^YXXYTSRSUUSPTUVWZ_fkfdcccgklf``eb_ae]``]_f^MBMZ^[\`caadfddca^]\[ZYZ[]]XSTWPF9:;<;988<:;?>=>>?>>BFIHEBGGCCGFBA_wxtvwtutrrrsssvuuvvxxxrmnttqpplc]a`chbX[[]_`^^`bb_^^_^\baZ\^Y^bf[aa_mmbɻº˰nZdOKrtV\^WVXVVUUTSRRPNOQTVNAABJO=2::' 6oz`[de_YV^hqthory|nga[XXWZ[ZZZZYXYVVXYVUTQPQSSQOOSUW[[XZ][URQRQQOKCB>;>======@FHGFECA@?>?Snystwtsuvvvutssruuvxtptrnptsqpnjg]^d_`e`\\\]^_\\^_^]^__Z[[Y\YVb~\Ya^knhȹβpZeQLsvW]^VUVWWVUUSSSQQWWQSXVTV[S:0<;& *e}eZaf_ZV\fit}op{nz{ob]YZ]XY[ZZ\[XVWV[][YTRUWVQLKGGOTVYZY\^\WSSTTTRNHGC@@BB@EIGABGNTXYZYYWSN98<<4(! #%'),/1113539:8>O\^ZX[TPYageac_SQZ_fcbdfca_`abbbbcceUWtľ`_e`bfcff_aacbaaXMLKNW`c`b^^c`Wzz`ilkmniigjkjkkhkkjkmjhmommnrrZcӀKƖmlvqg^WTTUWWVUTSSRSRQUZ`celd`cinojacc`agf]\[[Y_h]CDT_^[\`dgfgd]\_aY]`^YUUWXURQOJA;88889;;;AB<9>?<;;88=@@CJQF><96:Ci}{ntwsvqtutqqtxwuvvtvyxqqstpoomlf]]\[`ba\\\Z^b_\^^^`a`]_[[]_XVhnV[`mhdŽϵr\fRNtwX]^VTUXXWVUTSSUSSTSPQUNNVV>1:8&*^xf`b[_[V[dekztpyp|}l`\WVZY][ZZ]\XUVX^ZX]XPRRPF@DCEOQRVYZ_b_VSSUTTSNKKHDCCA>BIJDDIORX[ZXXYTL8:>B>0! #&')+/001357:;<@JV^UU[UPW^cceg_QS]_cdfhihebca`acdb`gVXtù]chbceabb_aadbaaXPOMOV^_\`\\b^Uyu^jlhjmkmfjmgjnghkjhlnmlollnqqYbрFŸƈkusia^[WUWVXXVWZYVVUSSV]beihfcejmmifb^]ab_Y[\YZc[DCX__`bfdeb_^_`_^ca\WY\YRPWWPH?=CB=9;=88AG=:?A<:><<969AHKQJEC=9CRm}zrz{tvvutssstusqsussvxtpqpmrvrndYX^_`d_]]^]^abaa^^`__d_X]]`^TapVbbkhgҾ¿˯r_fPMttW^]WYXXWWVUTTSTRPNORWZTPRR?6;6$!&[wi_^YZ\[\chik~ut~wr~xi_XXWX\_ZYZXVXYT\[YVVWUQURDKVZ[XXQO[bceac`TRY]cfifa^adeeddccbbeVVsõƷ]cfacb^a^c]]abb_XMKJMV_`\abcc_[|r^klikmhknjfikhghkkknnkjknomor\dπC~łgswlb^[VUXVXXWVVVVRTUTSX`ghgedfjjigcba`ab`\ZYY]bXDAV_bdehfjd^\^`_]\]ZWX[WPSVSKC<:?C?::<9:AC=:==99=?<:<<>ENJMOOLKMQ`swqsurrttttsssrytrssuxzsnnmkptooja]]\[^_`a_]`a]__\\``]]\X_^a_U^kW`aloeѽ˯r_fPMttW^\WXXXWWVUTTSPQRTUTSSOPVT<099"!" %Wsg`a`caZYaedfvwt~zwyi]VY[[[ZZ\ZY[YWZXYYXXXTOURE?IPNNNPTWYZ\^\TQWWRPSRPLGBBBBEHJIGINSWXZ]\WOHA8132(  !$'-,,,-/246888>JUZZXXQO[bcb`b_TQZ^ahic_bcbedddccbbbUWuû]`c_ccaecb_acaa`\MLKNV_`]_`ac_\}v^ikiklinkggigfjlhjnlkkjkmomor\dԀGz˽zgvuj_YVRSV[[ZWTSTVSUVTQS\ddghfcdhkhcaa``cd_XUX^aTB@S^dgdeccb`\ZY[]``\XX[ZVTSKB>::>CB=9;;BS^gidb`[]_^[YZ\_^[VUVVURNC<;;CB?@@<;:<=<;:986:;;>EKMOPLEFOPLO_szrsvssttuttssrrvwuttsxojihptnficZX]be\`b^]bb\`b`^^````^^XX_]`\^Z[]`gjhþЮq_fPNutV]\VXWXWVVUUTSSSTTTSQPPOUT>396#!Poeaega\UW_ccfamztytsqe[WZ[YXVWVZ[VVYZYZYWWXXWQUOHIOPQMMOTWXXY_]ZWTSRRQQSL@AGDDHKJHINTWY[\\YTQK>40-07=?3&#"#&+),.-+-274578==CLPNKILNMJMYq}wsvsttssssttwvwwtvxxtmkjjprkfjf]\`bb__^\[_b`_^^__^_`]^`\[`_\a__\Y`jijĿŽҭp^fQNusV][VWWXWWVUTTSRSUUUSPOSPTTA7;5" Qqh`bb\YWZaabhcezuzooynf\WYYWXXXY]\XX\[VXZYXYWUQWVNKNQSNNPTVVX[da\VTTVWWSTLA@FBBEIJKMQTXY\\[YWVQE;40<<>??;98:=@CEKHGKMLJIIXp|ursqssrqqrstwtsrruxvnjjkjopiehbZZ^__^]_a`_^]`\]aba`^]^_][_`\Z[^^Y`g`jЬp^fQNvsV\[UWWXWWVUTTSUTSRRRRRRQWWB6;7#Psja_^^[Y]a__fecrypn}tmi]VWXXXXW]\YYZY[VXZXWWVUTVUQNOPRQLLSTRU]_\[_`[VUWSSOEDFEGIJJJKNQXZ\\[ZZ[TKHEANeoXG1#!%%$+,-,+,035579:>HQUVZSOX_adbc]OMV]dedcbedaaaabbcbc`SUt^cfbddaeb_`b`^a`XNNMNT]abc_^b]WzvamogdhjkhikhgjnkjkkhillilmlmpZb׀RϏq~yYSPTVTTVOLLNPUam`YUVUSV^aceebbgmmc]^_acd^ZQR\ZH9BQTTVWXSUX[YURSUVTRTUTOKA?97<>=?BEB>?><<@@AB@??@?;65?=;=@ACC@=?DB>::;<>AHIJKID?(%**('')-145679:99BLSV[TOX^aa]^[RR\bdggcacc```aabbcc^RVuû]`c_bb`dabaa_`fcVLMMMR[`bb]\a]Wzt^jnnok`chmkfhkhihehmnklhkmklpYa΀Oʑz_V[PLWTILMVXY_\]iifaZUTWYb_^`_\aik`V[^^ce`SOV^UHLXYTLKPQKPQRSSSSTQRRPKE?<9426;>???A?;=?@@DB>?DEA?<=CC;9@DEFIKG?;>8469::;=?@?@;:67<=:834:@>?EHFHHD>=@Dizxtttuosnmrroosvwutqqurfahnoonrgda\]]W[_`adhhfe\^aca^^^^^]^a`]\]]]^Z`eXqþ¿Ȭo]fQNvrQWYVVRRTUWWWVUTSRRRSTUSQQTE247' Kpj`eda^ZY[]ae^bfeizyv~|rmg`\YXYYWUWWWXYYYYZXYZXYYVUQRUQNMLDGCEOUTS[]][[]]\VSV\\UNLOONLGFKSQXZZ[]]``^[YPSbe\WB)!%%$'()+-023568856?INWXPNY`_dba\RQ[dddddcbbc_baacb`bhTSqŶXad_ba^b_]ab]]ecVLLKOW`b_`_\`_Z{x^hlhfiihhiiijjjfjljhilnlnlkppXgˀ&|Œn]a]YY]_agjfhbWTQVcefc]YYWTZcaYUMN\gi]TUV]b_STZZMFRSMIKLJKNNQTSNJKMJGBAAA?=7447;<;:>><;;98:<92046454138;@FHINME@CLPctxvvsrlpooqsrrswvrsuvxrgaiqrolnnh`XZ\[`he_`jmigbcff`Y[`^\_bb_^]\[\\Z`f\túʮn\hSLtqPWYSSQQQRRSRQPYXVTTTUVPNQUE247' Hnh_fg\]]YWZaecddbdrz{yqnh`]ZWXY[[XXXXYYZ[[YYZYXXVVRQRQOLGAECBMWWQW]^YUW]`WST\_ZSQONNKGFMUX\]\]\]ab`^\TUbe_YC)!"##'')*-024568965;DLUWOOY`^`^_[SRY_dcdec`adbda_ba_bhTRn{[ad`bb^b_`_`_^a_VNLKMV_b`b]Z`aZ{v]hlhfhhhjifjnmhkjjkjhjmknljppXgˀ Y纂ja]fqmbbddh_`\YVPPYflme]ZZX\`]WQKKS_kaTRTZ^VLQ[YLFRPNKJJKLLQSSPLHFF@ACCBA?=:7578:87>>>?>823:955873283147vegkwxnbbededhf`]XQLU_hie_XSXX]]SKKJUgeXSTYZNFNXVJDMNLJKLNOOSSQMGCAA>?@AAA@@87669::9;;;<=85777559:8853358:>BMKLNPOPRNYnyvoqynpssrrtvouxytqtrjgkjjnnohjwuc`g`\aaahllmhdcec]\`^`ec]]a`\[]]]``Yz¿ƫq_ePOwnMVWOOOPPPQSTUVQRSRQOMLMTZVA9@8( @jiaeecbbYLN\eea_`bgt}trpne^^]YWUWXYZZYWWY\YXXXZYXXVVTRRPMJG@@HQUTQWZ`aVJP_ba^WVX[ZVSPMIDEGSVYZ\[]b\[XYUT\a[R;&!$&'&'(*-0346677536A><<>AA@=;;:99975;:76889<><647:;?775588>>==<<>AA?<@?;76898<=<:;<;<@?;9:;:<;;7588;BNKJIJJJJFQmvstqrtsqnoqstttsuwyzscelljnpsxxx}r\[b__ccahlfggfebaabbccaa_]]_[][[_][¾ͬp^eQOwlLQSQQMPPONOPRSSSUX\`dgdjl^@:D8!!Fqk]adg^bcRLXbbcbbbaixrsupb[WUTWYYX[YWX[\ZWWYWWZXVWXWSRSQQVH9;KTSRSPVY[cljb_\\^_]]^]\[ULCDHHQWX\^_`^\UVURY`_N2 !$#!%&(*-1457422116>;88:;::=<:;<;:==979::<9978=<<@DHHDBBB?@Rswqwvvonmortrptuuurswtkhlidhoxyzx~xd^^^_bYMXlqihfdbb`^`ddcc]Y]\Y]\]a\Zʺ¾ûͮn\hRMtlLPRRSMNOPTX]bemoprssrrpqqbA8A8%! !KukY_fdZbgWNXaa`[[``eorsvo`]\[ZZYVTZXWXXWURXZWVYXUV]YSRSPOUM>>NUSRPQVY]grrjj_[aaYVY]\[ZTKFFLOU]`\\bc_XXVSZb^K.!&$"%&(*-1458300016=BMRNOZ_]]_`ZRU]`adeb`aba]`abec`biSPn}Ydfbddadabdd_^c`TNLJLT]_\^]^]X\p\iieggbcghefkljgknmihjmiljinnVe΀&,3qeo|\^``][ZZ_efcelmhnmjcYQPRPX\`bXG?JTbeZRRU@BLM@@LRQRQOMKGA==;89=?>9<=>@?=>DB;427978;=<<=?@>><99;<=@98=>:BD9@`qpuwuvusqqporwuvuvuuwrfbkkeiszyytxp]\`acWJShoiojcda^bfdedb`_\X[[\]b]^ýɨq^bQPvkENUPMOJPJCHMPUTTVXYYZ[aYSJBGF8%! !Ryn\agddec`]]^^b`^bbbh{|oqzq`ZXXZYUTUYXWY\ZWYYYYYYYXXX\VQTROTSFENSRQMRLQ[akuwqrj_\aa]]ab^XRJCEQWX]`__`^ZZVOXdY=($$%$',/1479752/159;FPSMNY_^a_]WQRY\cedcec`bgd_ae`\_bUTl~|Zce`bcafd[_c``eaRNNIIT]`ae^Z[XY}l^kigiifhiiiiijjkiffkkjmjkijnlVj̀"12]n}iY]ab_\YX\_dhklkimjknk_SNPSV^e]K@?I[d]TNG>CNK<=LSSSOIFFB>;>>==???==:;AA>=:=<769:959>><:;>B?<::;=@=978:<=@BC>AB@=5Dgyomvxssqqrqpqtrtttrqtqbdllntuusxzz{wi\^^`aPFYmrkehidbfgeeda_]Y[^\YYa]^ž˨q]bQQvkFNUPNOLR?! #! !#$&' '=>????>;@>:;AC?>:>@=<<:8558==977;88::89<:50025565:;><9BGPcsqjnsqqqstsqqrtsqrttun^bhipurrux{vsyr\]]\e`JNfojhifdfhgefeb`^[\a_[[b][»ʨq^bQPvkGOVPNPQXJ-"%!"!0BBLSG)" !Nxphoghdbdda^]c`_]\_fhpyzrqum\WYZYWVVUWXWXYYY\YXWVVVVWXWYVPRTNQOMPTMGINQPNR[hurvwpf``bc__bb\UREEKU[^_]a]XZXRY`Q3" ""%)('*2750/01/-/7>FPTMNX_]]ZXSMPZ`fbcc^_b_\___`]]dbORr||Yce`bb`eb\\`aab]RJLKNX_][[Y\^XXp`mmjhgdjjjhgghikmmkhhjijkijnkVjʀ/+3fuekc_^[WVX^ccadlmihlhmmffmok]UOTadYLB?Qb^UNB:FMC5;IMFE@<<@A@<:8<@CA=@?<=AB??>??=<;72238<933720278547=<6015525<<<;E]mcao{urvtpqstsqqqutqstvvnbckprsrxzvxwsyvc]d_ajYGUikljgfggjiihc`^\Z^^[Z`XVȽĿʨq]bQQvkHQVPNQlh\QLJFC9<;5/--,++8>=OT<% ! Rsjnehkkgcaa`b^``[_feirwsopi[TY[ZWVTRYZYXXWX[YYZZYXWVYX\ZQQURRKKOKE?9EOPMOT^lkottngfjle`ac`YTH?DS[_a``]Y[UQZbJ,! !"&''*-0013////.08@GQTNMX^][YYWSU[^cdffa_``]``_a_^beTSm{{Yce`ba_daa_`a`a^WKMJLV^]\_Z\`Z[~palhejmjefffefhjggjjgikijkijmlVjʀ+00Ebwq[cbZ[[ZYZ]`ggikkiggcba`^^dkjkbTT``UG6>GF>>=:<@A?<:9=BCA=??=>CC?=?>=;;;975359844651155226695,*02-994>C2((++,+(++(%')''-S}tjspglldafgd`]ab^_dbcksqjig\VY[XXXYXWYXVVVVYYZ[[ZYWVWX\]WRQSSPROC94/8ELMOOR]cekrtqqtvsmgda]XM?AOY\^^a\XXSQ\aB%!!"#%&&+0/+.63/-/239?GQTMNX^\[]`\SQW\cedcda_`bc`_cb_`dUSk{ô{Xce`aa^c`b_``_`^WKMJLV]][_Y^b[[o\komihidhljfeiojghjikmljkijnkVjʀ.4-8UggzX]_YZYXXZ^beikjiifbba_\ZZZ[bjpgVORSK>BW_RC:==@?<@?ACC@<;??<>DD>;???=;;<=>625;:6363243/.02/.-,+-03,6azvstdZgzvoqrqssrqrrrsuuvssurifmnjrvstwzwsvyxe]]]htcT]jmjmjefggigb__^Y[[[[^VWǼ¾ƽȨq]bQQvkJTXPOSVNKPQOG>HFEKVXG2#!(-(*0/'#')%)/..,'&*-/4-8c{ptrmpokkmia^^ab``aa`fomdce_XYYYXYXWVXXVWXXYYYYYXXXXXXXZZSORORRQMA2,.7CLOKJP[[`jrtuxtywnc^\[O@HRTNMX]\\\]XPPV[dd^]bc^_cea^cd`_aQQn|zXde`a`^b_]]__^`[QJLKMW][YZW^cWZtakjffhhjgefhifcmgefghiijkijmlVj̀ .1*5Wlhe}TZ]YYXVVY_dbdfefggedlmgehbVWVcqkYJBB?CRZN>8;A@;;@?;@A@>?A????=>BB><=>??><<<=:77775321110.,+/*)-.++/,"-Zvqosl_fyzpqwrtusrstsvustuvwpmhjghqutwttrnouxzh\]grn]Rdmieefhffhgb``_Z]]\\a[Z¿ǼŨq^bQPvjKUYPOSYRQQMIE=JHGMZ\G-*$$*,)*-(""$#'.*&)%"&,,,.Af~{uslnkhgfb_\^``_`__b_bki]]dc\YWVXWWVVYXWYZYXYYYZZZZYZYXYZUQRNOMPVN>4./;KOJILSSV^fkoqqvysi_[YSE=GV\_e_XVXTT[X4"!&($%(+-/1335/-00/4=HRUNMW][_\ZVQTZ]ca_^aaa`^ca]acaa_OSr~zXde`a`]a^cca]\a`VKMJKU\[ZYU]`TWnanjgihbdfiigfghigfgjkihjkijnkVjՀ '-+2M`^b`sT^WTVY[[\^```__aehjlpmfeihbZSVagg[J;ABDHB:=9?@<<>=9AA@>?BB?B?>?@>=?>@?=?>=@==@CDB?=<;97652/11/--.//*-,(*0.&$)Vvvqmnffntwuporsrqrrpstrtssupqmnosvu{xmmtrqvx}u][bgpiW]glhehmiikic__^[_\XXa^^ͽ˨q^bQQvjLUYQPTWMNRJDEFBEGMWZH2 $%'+($&''$&*+($&&&(,+'(3BRVSVWPRG@>;8?K_b_\__^d_`hgZZde]XVY[[YYWYXVXYVTTVXZZYXVXZ\][WRQUPONLLJB2,8KOLMNNNOSZ`egjmsxuldaVI>DSXZb`]\\SOTP1"!')$"*.+-3633..0-*1=IRUNMW][\\\VMMV\__de``cbX``]^aac_RVszWde`a`\`^^aa\[a^QONHHRZ\]]UZ]RVo\hjhfhiefhijihfhifdijgfjkijnlVj݀+303BQWRNogS[XYXWX[afjjjgcekoomlklic]ZSOLLJKLJ=A@>?@AB??@CC@>>=@BA><>AA><;:;;82.0).Lab^ab^_`cchdWZeh^^]\ZXWVYXXZYVUVVWWVWYYXTTWYYZYRTUUPJNOH905BHHJLILNOPTZ_cehilmjd]PFHS[\Z^_Z^UPWJ"!###$&*-..///-0.,--3=KUTNPTVZ^\[YRPU[`^cgc__`a_`cc_]_hQSr~ySdd_ab`a]a]\]_a^UKKIKT[]\ZX[\RYnbhhkfdkkeeiifeclkeafkkjkihkojUl܀*1-0>MRPM[`V`]XWXZ^ekojkjghkjhgihc\TLGEDDCACC@C@=@DCBE?@?>==<<@?>>>?>><>AB@?@C=?>=@?=?@@@AEGC<;87886332221/-+).0,&!$&(Ajzronhgpxxtsuplnoorwwuttsrstqnrwvsplosw{|{xwzxe_fmpgY\ikhijggijf_[[\W]rqh^bͽ½ƥm`bOPrjKTVNMQYOLMJJHBIPRPQRLCGGGE@:4054333445667778887>>IH===;I^caa_Z[]`]a`WYa`\]]ZWVXZWVVXWUTTQPRUVUVYWSSXYYVSRMMPOPQOH<2:INJIHLOOLKMPW\_`befdf\KFRYYYb`X[WTT=##$!!&),...022-/.,-.4?MTRKMRVZ[\[WOOV[dbba_ab^]]^aba`abPSp}yTdd_bb`a]Z^a``e_RGIJNW\ZX^Z[^SYh[fikifejihghihgkihimkhhkihkojTl؀)0+,:HMNPNyޛaY[WXWXY]bgjddccecYN[]XLDDED?CGECEC>BCB@A@@A>@A??@@>B@>=???>@>?BC@?@<;76:;;=DB@CGID>8556531010/--,--4<3$%&:auqmoihowxursnntuppssrrtusrrnmswsonmvtsvz|{xyk]dork\V_hhgihghgd][\]Vf{dV[ξ»Ʀl]`ORqiJTYPLNXQNMKJID?9447740***)&!#"! ####$$$$'(*,,,+*,330333CU`a`^X[_a[^_\]_[Y[\[XVVVWVVWWUUUKOQPQTTSWWVUUSUYTNNRQNOOMKEBEB??;:;AIOOMMRVUVZ]_bcTIQWWZ]]VWXYR3!##&))+,,-023-.-,-.6CSXUMNU[^Z\\UNPX[d`bc_[_d^_`__`_]]PUo|wVdd`ab`a]_`ba``]TMMJJS[]^_Y\`UXh\ikijhadhhfigefgllgfhjkkigkojTk̀+1,,9FKTVKeľļû`[VXYXWVWY[]^[XY\WF5?ILE@BB?BDFC@CEC>CC@?@@AED@>=@BCAAA?>>?@CAAC@<=B@;986:=<@@?@AA@?:7554100-,,-048:?J: #$6[ttnokhmuxurrnotuqprnpruvtrswonnjhmqxtprvzzxyyt]aomlcWVajifihggc^\]VPhdV[οúƨm\`QTskJU[SOPVOLPMGBB=&""/N^a_^Z\^_[\a_^]Y][YXWVUTWVVWWUUVRPQTVVUTVYVSUVTVWWWUPLLLJJNOIFB<82.222?HFCC@:BAA@=>AACA?ADA?AGDA?>=>??AB@=<>@B@@@@>>?><=<88:98876557:32120--.3457;AEHHP?$4Xuxoplilsxvqqonopppprsstvtsvqebfhkrwssttwwwwxs|zfckhlm`S[lofkiihd_]^]Si}`T[¹ƪo^bTVulIRYRPRXLGMK>;CH&  Mab__\ZY[X\`_ZYX\ZXWWVUTTSSTUTTUSQRUXXWVXZUOQSUYY[[XSNMNPJJLNQRNGD>969?DHJKJLNLHEQ[TLRYVT\]WSN?%"# %(%(+-.0221--,-./BBAAA@=>BAACB?<=?@@?=<==><;>BC>99<;874364211001431000037BCDEHKNOLOC&#1Rt{oqnjkrxvpnqrppsrmqrprvuruqdclqpqrstuvuvwywux{tjgiingWVfmfjiijf`]^[W_ria\`ĪqadTVvkHPUONR\J@FD78KJ(F_b^`^[YZZ]b`ZX[UVWXXWVVUSSTUTUWQSSQRVVSTWXZWTTX[ZYZXRNRJMMOTVSTQOKE@@CFEGGHLNLGJOVRIRYQSWYWSK6! ""%&((*,-/10.-,+-/0>SVWVQPW\Y[ZXUQSXZ_abca]]b___^^_```ORnztYdd_bb`a]\\]]_c\PKKIKSZZZYZ\[QX~c`hdgiffbcfgeijdchjjkkihjgfjmhSjþ -3/0=KPQXTaξRPTPPPPPPONMDC=648<<;>@ABCDC??@A@BB?;AB??@@ACBAAAABC@@AA@?=<8>A><<;89:8564243102200120...05;JKLNNONNMNJ1 *Ioznqokjqxwpptsopttonqpqurotmabntsrqsttsssvxwwwzyoijghi_TZfihhijf_\\]_[[X_\X¿¨pbdRStjJSVMMRYI=@A6;QI&<[b]_][[\[]baZX\VVUUVWYYUTTUVUVXURQSUTUWWTUXYWWVWZZYYTQTPPIDMWZ[VRMIGFECGJJJLOPNQSVQKRWQSRTVTF0"" "$$%&)**+-//,-++-/1@WVTRMKTZWX[\VPRXY`eb]_b`\ca`__]^_]PUn{tXdd`ab`a]a^^__^ZRJLLOW\ZX[Z\\RYg`kihigfehifeijhgggigcemigeimhRj+2-/=KPQRGeǰݘ[SUSPPOMJGDB9744:@>8??@CCA?><=@@@DC=BBABEC@ACCB??ACBABDDCA?>SG":]e][XWX[WX^^XUYWVTVYYXUTSRTTTUWSQOPTXYWSSUTQTWTNY\VVWVWSRNHIX_Y\[XRMJJLMPPMLOSTQXYSOSVUVRUXS@(   #'(&,,++-0/.,+*./1AZZVRLJU]ZV]aXPRYY^bb``_^ab^^``_`d[RYq}sWdd_bb`a]]`a^]`]SHKLOW\YVZX[^TY}c[jjdeiidhhgfdcfjjhgihfgigeimhRiµ&003BMLMMHşPGULIC;64321::::::::<<<<=====;=@BCDEB?@DADECAA@?B@@@A@<98<><9986.35300/.1/14630/53.1646DIJLOQQPOQRT? &>buropnkmsustsqkhouspuustrrvo\U^efdbfnqqronqox|ulhgekimo`QZqmfifca\bZ[\YX^^W˿ļƺracPQxjJTYQQUWFCF;3@PB$ 7Yb\]ZY\\ZY[]]ZWWSRVZYVSRPSY[WTTSMOSRUYVVWWUTTTTVTRVZTPVWVQKLSXZ[]\XUSPLTQRUVUVZZWWSKLSUOPQTQ;$ "#!#&)*,--,+++)/.-/9UZXUMIU_ZY[\XRTZ]\]ab^]__`__]`^Y_\OQhx®rYfd^adcb\\`_\`c\QKLKLUZYXVW\ZOZ^aqg`ehedhjhedeeiggfehkhflhfkhSl°Ⱦ '--1=ILMFd俤‡I=E>;8448<>?::::::::;<<<<===;=>??@?=DDCA?@BDBDCBABA??@@?<;;<7679732342//./12-0343211.224757CKLNQRSRQPQWG" %6[sqnqoklsusvrpolostmqsssrsun`X\_bb`dlpmnqtuwwk[YgolkiilgWUfmfhic^]`[[\YX]^V~ʺŢracPQxjJTYRQUPEBB61@RG& 7S_]\[Z[XXY\^\WRUTTWWURPQSVWVSTVXVSRRVVQUVXYWTRRWUSX\WRQUVSLHLU\[_a^[ZXUQPSX[ZZ]ZVXXPLORPQSVN6  "#&&'***+,,,,)+,-63.ASVQKOVY[XYYUNOUW]aa``^[^_^^]`^Y`^PTo~vUfd^adbb\^^``_a]SOMHIRZ^`ZY\[OZhbjbbikgehjheeghhghfcfjjglhfkhSlǺ#(*,3@IM>B߼м~J573;855:=?@::::::::::;;<<=====??===FFD@@ACBBA@>@A@>?AC@<888977996441/.01/02021.-156.455768@MNPRTTTTPPYQ,$-Pprmrqjksvqtqssrqrqpquvtrsslb\]^adaegjkotuprrgXZhoijieim`SXjffib[]][[\YW\]UϾ½º¢racPQxjKTZRQUPE@:11BRI'!$$7J\^\[\Z[YXY[[XUWVVVUTVYVVUTUVWVYWSUYYUSVWXVUUUUTSUWWXYXQSSNGGPZ[_ca`_]ZVVY]^\[[^XWXRKMRPPRTH/ !##%%&%+))),-.-*++-82%+IYUKRXX[XYYTOOUWacb`b`^`^\^]`_[a^PVtrUed^acab\_Z^a_a^SLKILUWWXWU[\QX~b_jdbdffcdddefecgeedeghegkgflgRlɷþ!((*:IKG.XپٱI19=A=989;:9::9:9:9:9::;;<<=?<=??<;=<>>@ACBADDB@@CBA?@??<9535653464/,/241..0/0//14546645756=LNPRRSTUQOXV4$%Howltsjisvqtutpmpstsrtwrrtslb^bdfgegehmmopnlmh`^dggiieflhZQ`gddaZ\Z[[[XV[\T~ʿ½racPQxkKUZSQURF7.*1@LG'"""*4AYaYZ\[^[YY[\ZXVWWYWVW[XWUUWYYX\WRRVVUVXUTUTSV[RRWXVXZUQPQPKGKQX\^``a]YZ[]`a`___ZXWRKLPNMPO@) % %'%%(*('),.-,.+,01-$ 7V\KMWXXXYYUPSXZa^^_]_a_^[]^__\a]NSsmXdd_ab`a]]XX\^b\NKJINVZY[ZVY]QY`_kggifcbcddfhhghihfcehkhkgglgQmöļ*((>BCC@?@AA=;989864121/020-/230.-///./245448547846>JLOONORUSNUV9#$Cn{mstkjptrsutolmrurnqsqptto`]eifebahnmihlmdgf_\`efgggefkdSTfa]`[ZYYZ[WV[[T{ɽracPQxkLUZSRVTG4''.;FF&  " /07VbYX\\[ZZ[^]YVWVVUTSTVUVWXXWY\WUROPUWTSVWSSVWVQQTVUZZSSPPOLHILUWY\cfc]bbba````[\ZXTNJJJLPN;$#$&$%(('')-.,*/,,00*$!BWPJSYUXXXTPRWX`]__^_a^^Z^^__]a^MRo~rWcd_aa_a]__]\cg^QKJILUYZ[ZUY\RY`akcdihefiieacgkhggffegiijfhmfQnźȾ+**:@3*Vշ[VgR;978:;97776767776779:;<<<>=<;=<;@??AA@@B?AA><===<<;:8743310220/1231.-00.0/144348425:948BHKMMKLPUTNSW@"#>fzossnkmrtqrrsqlmsuqprqpruqa[ce`\[Zhonnkc]_fd\^ikdfehe`hiZOaa\^\YXXXYWU[\T}ùûracPQxlLVZSRVXO>315>KE%#" 3-/PbZZZ][YWX[[YVWTRSRRTVSSUWXXXYWUTTSTTRQXXRQVUPMQUTSWZYVTQNJHILRTV[cjifhhfb_`_]\]YSQNJJILSM6! !"$%%&''*,,*),.*04*!+FRPU]ZZYXTORVV`cbbca^_`Z^_]_]`_ORn}sSbd`aa]`][`^[_bZPHJJMTYVTVTX\RY}|]aj`agfeggeb`bdeihfimlgdiieinePoŸ*-1;5.7ί~|xjgpdefghijjgggffeedchlidiwfi~Z<:878741555555555678::<<=<<<<;;<@@?=<;=?@BB?>>=;<<=;830.1//10-.11111100/.0477544436975;CHKNMJKPVTPU[I$$8[sqqrqmjovtrpqsnmrvtrstqptpb[]\YYYZfjkm_NLcgbY^knefcdeace`SYca\[XXVWXVU[\U}½racPQxlLV[SSV[SHCDBDNA#!5-)I^^]X[^ZVVXZZXSSTVVUUVSSSTWYXVYTPQUUUXUTTUSPPRNTWXXVUVYXTNJIKKMRVX]cggdffcbcb^`]TONKJOIMRG0"%()&'()*))))--42" "/CMX^Z[ZYTQRWW^aa__]\_aZ_`]^]^^PTn}pTac`a`]`]]_`^^`[QILKIS\]Z\Y[ZQZ|_aichkfdedbcehgdghgfheehjieineOo¹ɾĀ'/9?23NҬƊ^XSROLLOSTVY[^_adca_\ZXW[[[[[[ZYhom|N976787414444444455689:;<:==::=>;><899752/-,.11.-.//00231.0423335774:65546?99=@=9<=;::97522441/1..120,-10/11./3368743664356558?FLKJJKMPRWROYY3!'OsrjnsnjoruttspoooottstomtobYXWZ^_^cdYH>CMZidV\eed\_a_\\`eWL\naUXXSX[TOUYQҿ»º¼sf_KQtnOVVTXXQKHKJECG?#+8) 9Z]X\^\ZYY[]\[ZVUUVTRSQSUTSUWYWVSQRQPQTVTOPUTNQPPRW\^]RRTSNKIFFMMLSVUV\`^\aa[YVPOSQIFJJLG4"! !##%%'*)()**(*-))24&"  =X^XYXURRVZa`^_a][`]`^\]\\a[JQnsTbaaabb`^\^b_\c`OJNHFS[XXXVYZT[yrW\heghhdchfdjjeegeggfgigklggkfTnκÀ0920)Pݲڟ^BLMMOPQRRPUYZ[]__^\aio{~ytoljZiڧhG67887531/////02354444567876788889;;;:;<=9;;8678722220/.032/,.11.440.12138765545688878=EKLKKLLMPTWUQXX8 &Jnslnrmjosssrqrvskqrqrtsruq_VZ^`dga_TEBJMHXfbVZcbaga]]\[[\TFN^\XXTUUUSRZ^Zsźs][PYulPX\[\WYQKIFBFOA$+6(-Q_[Z\_][[[\ZYUUVXVSSUVVUUWXYXVURPPPPRTSSUVVSQNQTVVXZ[WUWWRMNNILQTRRTVSTVXZ^]XRLLPNHFJKME2! ! $('&))*+)')-+.53#":V\UVVTQRVZ\_^]``_a]`^\]\\a\JOk~nWeb_^`aaa^`a][`]NKPNLTYXWXVYZR]xa^efjhecacffcdghgeffdfhglkdcfcToνƀ55//9}ԵwGILMNOPQRSOTVSXbgdieer}uppf_὇X=677764202231002422333445;746889:6:<99;<9898645660010123210-,055266326668955764477678 $Cftmnqmjouysonqtqlnprrpoqupd]__ade^LAFLIHKS``X\a^[[XWWYYVRIDIQQORUUTSRU\^YnrkjTPmnPVYXYVXOIIIFIOC$+4+(Kb_Z]^][[\][ZWUUTSRUYVSPQTUTQTTPMNOPSVXXURRTUQSVVUTWZ[WX\XRQRKGLSOOSSRQUVSVWONIILKFFIKLC.  #" &*)&*+-,)'*.-380 # !7SZVVVSPPTX[`_[_a`_]`^\]\\a_NSnmZaaabcc`]\_^Y[a]SJLLLQWWUXQVYNW{u[akcbacdfbdfdegehfhhghihjjdafbRmµǀ*5213ZaDVMNNPQRSSVXRLRbe^_bcr~wtunazԕU:77654200.//0/12401233333755665555:;88<;7677654331/-0463110244101//25358796677568458=AEFFLJKRTRQSY[WY[F"$=_uompmjnutpoppqqpqpprprttnhc``c_SHEFJLIGIMY]Z]^YUXYXWZ\THAHLLHFKSPTUSRURJY¥secMOroQVWQTWaRFGJJKNB$,2*$(A[^Y]ZYY[^`_]XUSUVUUVXSPRVVTRUTPMMOQVVVUTSUWXVUTUTUX\WRUZYUPMPGEKPTSOTTVTQQPJNJIJIEEHLL>*"! $('&()*)'&(+-69,! #6S[XXWSPORV^ca]^`^]]`^\]\\a\LRooVdb``aaa`^b_[]aZRMJHKQZ]XZTYZLYuXbkcdffefeehgfefccceegijgiefidQiр#84+?ޙQ@PNNOPQSST[\VMR`faells~xwvq_iۛT7875310//-,,-//000112333266433565878:;988666543332.-142/-12685/.0-.476686898679988:=AEGHHMJKQWVSQZ[XZ`M)$7YsomonikstusnjotttrqpoprqmfbaddWD>IMGEJLIIRVVXYVSZWUWZXMBCIKHJJJMMNRTPF@@HP^y;ĤsfcPSupRY\TU[]PGIMLJK>#/0"#"0LYWYWWY[_`_]ZVUVWUUUYVTVXWWWXWTPQRUZ_VQUYVUVUSSSSSW\YTTY\[VMRNGGRVNGNSSQRRNNQMKJHEEFKJ9$ !!!%&&&((''()*),66(! 8U]WXWTQQTX_b`]__]^]`^\]\\a`MQk|uYdb``aaa`^ba^ab[RMIHKRZ]YWV\WL^vX^hdifdfdbcefdefhgecddfggiggleRj€6,)gῴЃLGNNOPQRSTTXYUR\sw}}uqov^\ԕO48741/...10./00/.001122235532488687789987764321252/0430/1145323203668<;779;;88;<:===>@CEHOMKMQVVTZ[X\bU1$1Rpnlonhhptuvtpprpruvurrrsmede^SJCFFFHJKJJIMPRSUVWUUY^VG?ACIKIJKKKLGJVR=5@IQPNQQMORPLIGEEFIG4  #" "')&'*))(*-,+.51$!;W^TUUSRSW\\_^]``_a]`^\]\\aaNRl}s[aaabcc`]]_``ad]TJJJMRVWWTQWZP[{nU`ibfe`ahddfdegfdeefggfgjkfeieSl(.OзsKSTOOPQRTTUURUeuz{spxc[΍O99730.---.../////00//0122.03676417764688564320/14211221366640021/5756:978;;;:;<===>><;<@DFLQQSVUQX][Zc]9"+Jmmlopgdmvsrrpprroqootvtsnda^PCBFGFEGKMKGKMRWXY[\[^a[L>KWXfZPQKR\`}xlz~dMQWVWZ\TLG<0-10#41'% 2IUWUVX[^^[YVWZ]\XVWYUTVVRSVVWTPPPSXRVYWUUTSPMNQQNQWWOMPSVUQIIFA>=>@JOQPOONMPNKGEEFGFD/ " !! %&%%()('(**(25,! #=W\TUURPQUY[a`[^b`_]`^\]\\aYIPmmTeb_^`aaaa`a`_`\QKMKNVXWZXSW\V[vjYek`fid_defc``cddghikieehiedicQj%>¤eCQROOPQSTUUVR]wu~trqf[ÀG7973/-,,-/010.---0/.../12236752368534655642231//2430..13452012124768969===;:==<=@>ABA=!&Ejllopgbjutrompplrupknppum^SLGEGFIFEGHHJMKOX`b`__b`TCWZWWVRNMPT\ecZ\b`[]`^\]\\a^LPj|oZbaaabb`^a]^_\`^SKKEHUZX\XZYSPa~mUcmbb_`gdhdbghdeegeefdbccfdfjcNf)fֺ~`CNRPT[WYXPXUPȻwq~xpxu`z;563/./110210...../////023666566663432321241/010/0.*-102773001145358:==>?@@?==>@BDEFDJOU[VSXS[eceeF#%:dqjfrlajtsswuppqrqtsmouukWFBDIHAHEDGIJMQMNYaacd_TLC><<@DKMRSH97?GHFTcS;9C=:BJFGRRU[VMME1/=A?@BEKKFFKKFEIB?AJTXYZRLHGC<9;-%&5-#$##5CMPPRVWY`[TUYYZ[WWVTSRRRROMNNKJNQSQQSTSSUVPLMLKOUTRQSVVSNFED>59EMNORURNOTSGHEEHAEK; !#" "$&''&'&%$),*+12) ## ! -DX^RUWPPRPT^b_Z]_^__``][\^_aOTn|lV`ba_`ca\ba`^ae^ONLHJT[[ZZZZRH`|kXdhbficeiebadggfigeffeefjiikn^Th .ZШT==?BCCC=>?ACEEFHJKNUVVYX[a_`dM%!$7`oieoj_fpyvrspoqnlpusqtwgL=CEBELEDFIJKKMONS^ecVGA?@DEDCDKNOROB64<@COa\C47ADB@=@IMGHLLJ<&)7<;>ACIICAFKIC?<:=FORUVTSPKF@:7@80-+(')'&'%! !&+/1467/42&&1+!"" !&):GPQRTX\VPNPPPPOSOKKNQRQUNJKJKNNONPSUTSTWRPQOKKOTRSXYUOMH>:921?LQRSTQNMNLGF@@C>EK: !"! !"$%&&&&&'%'*).1.#"$ !0JZZVVSLNTQU\]]_`\]fa^YY\``^aNTqjScbbccbaa^]^`abZMNMJLRUUUWY\XK`zjYdlfdfccehhdbeebbhkhccegeeehl^Ti#&/;Ka~[F[eNT^\_\RXgƹvo}oqo]y۽q9373102321321//.///00-/45234566544652132/.0--031.,.//14335324547998::;>?=>@@@ACEED=?ACEFFFGIJLTXWW[Y^^]eU,"3[mkeni^cruttqlnottqopopsfMCIIABHDFGGHJJJJLMPTNC<<@E?ABBDD?987;BHKOS_gmidb_ZYQKJJGGJ69>5'(-(6@FEFJJFIRSIA>7-,(!'4-$!$:NPMRXXSPLMPPOQSPNNPRQPROPOLMQTPOQRRQPQRNLMMKMPPNQVUMIID97<85<;:;?EDCCDDCB?ADGHHGGGKLLQWYXZW]b`gZ/ .Uloink`aupospjmposqnpqptiREJHB@CFGFCDHKJFMKEA;;B@>>???AFGDJNLSYS?57FWd[C7<<:66F_zq]G<<>>?DFLRTROQRQLJB>CCCOL4!##"##%&(&%&&(**/GSL0!)K]VPUVOMPPW[ZSPUXVVXUUY[[[\]OUmzfYcbbb`^^a^]_^]]ZRILKKRYYXTRWYN`~t[dle`dfhcdccfifaeddggeegddein`Wm!(*)*.1;HaťTBPQNT^\`^U[оcp{rgh\sҰi;87544653122210.--/02430/021111122010/0/./--.00//0243352/152368;;89==;;:;@EDCCEEDCCDGHJJJJKNOLKS[_ZX^daf]5)Lhqjnka_msnknmnpnqpqsmmulM@@@CHPHFKLLWdXD;?OdcJ7:96=Osi?7?9(-7859;:;==946<91101578;@CLYafhhglfaa`]\]dabdirmXO]bYXekg`\TJB=>A8CLMJHD>@50/.,+(%#  "?PHGPPMONORMHJKLLKJKNPWVVRKJOQRRPMLLLMNMNNMIGFIKNNKIIKE?@EGHGDKT[YURPMJIB?@>GXK/ ##""#%()$%)*,+).G\eN-"'@[YOTWNMOPX^SHJPLJOQLJKLIIKRITm|iUcdb_]_`_[]_\Z]ZQGJGENXYTWSXZQ`zmWdlffgbdfgfbadffbcfhfbchfffim^Ti"(+*'&'/08G[zپ}}~}|WERNOT[Y\]U^׻og|shj^rɤc;:765677415433222210154/-/-.01221000-.00.-,+,/2333332331/153589;;99:9:>??ABABDGIIHHIIJKMMNLORPLPZ^[\]a_daA %Aaojjja[dopnnnomnqpnomnvjO>@DFGHCEFFGIIINPMF?:DKHRMAGRWXc^WIDU^P@KTexw\9.73#/6539;87/9DD8-.60+).368;<@DL[jleb]\_a`_a`_UVdbQI^kpg`cfcT]a[WYVOHGHJJIHJSga?.43%'$ ?98<>;=>?CCBCFFFHJLLLPNLLLORTQLMPMNTXV_[__bgR$"!6]rlknf]cjkormkknmmopnothPCGFACHFCCFHHKPOKKIA@C@BBCCA@EKPLONFLXXSZ`VEIX[cghmvwomeP85;0! +3218;87748CF;.''!%,1358BGHRdjefa_aa^]^[^TWjbLHO\b\TUWV```_\[]_VMHLPMFBWiiP8001&" ""!CaSJTTICDD??CCCFIIHHKONSXUNMPPLONKIKNPLPTWXWTRUPNPPORXTUTMJNL@D<8=CDDDDECHPoE#! #$$$$%%&&)(*)))-8=H\g^M6 4RWNTVMJMQ[VB-+0,)/2--6<:7667823653441130.10/2+*.0../++****++,/2676544011357889<;9::;>A>@DEACHHGILMLNQQQRRPNPTVRNPQNQ[Z^X^^^fY- ,Yrkhnh\`kpooonmlppmmprvkPDGBADBEDCDHJJJJLJECCCACA?>@DFFLJMVUFGZQT][E@U]lld^bbZTZQ?100+()45-.6947::++(%#!!!!GjYMOOJC;8:?A@GIIFFIJHLGPYSJJKHMOMNTXYQRTQMPSQQUUQSWWUWWSNLLJGFD@=<>CGKMO\e}F!%!!!%&$%))%#)&%&)49>;E]^QI3 >XZSQQNINTP;1**,+)*.,**.4;?G[mp|mXSTTUUTRRQNOPNNNJIAFNTTQUVSVXN`zjPafba]]d[X[__^^]_`aaaabceecbg[Qg%&"#'&$%)++.560-4:DZŭw~z|y~rXMRVd`b][YS]мe}qrjjv|xxrppxtS9==<98677875542221042/00.-0/12-,,*-+)((+.16300220-/255458;9;::;<<@A@BFEBBEFGILMNPSSTSRQRRRROMOPLNW[]X_^]e[0 ,Snlhok\cmrppommnpnlnpppoZMGAAFFDEEFGIJLHKJD??AB?@A@?BFJKFFS]TGELLPWOENW_aZRRQMKKH:-***+-*,028:516:FLKKJHIKKJEGIMRNJMQSSQQTSPOPQONSYZY^^XUUSOVXWRMLKIEDCCBCEFJRRVWq{A%"""$%&&('&$*()()49>;@LLPS=$+JWQUSQMLPI6##%''%&(''&&'*-/7@JQbiUUTTSSSSSSNOPNNMHA>DJRWTWVSVWM`{j[hkc^VQT[ZXXZXY^ZWX\YUYcddegjZSpț&% !#"!('.LhpqrO48Jgɷ}vyfbgVQg~pkc[WSU˸g~tieeimkkmkiiqpQ79<;97777742474211/2/+,//.,)*+,./,%'+.23339730..131477558;:;::>??AA@AABDHKKKLNOOQSTTRPRSRMRQOQSOOV\\X_`]d_7 *Hhngnn]]ionnonmimlkoqpqpdVKDCFHDFHGGGKNILLD==BEADFB=;BIGIPW\^R?DGPZREKRQPHCFF?CCLdmkjigfecbfbei`LBBCA>?IUZYPEA@@?=C^XI=;:Pm~eRWVSRQRSTROPPMLKG>>DHQYWVVSVVM`{jVbfb`XQTPVY\_[X\Y]ZQPXaeddgkmYUwڪ$#! !)/&Hݮa1.CWi|y|oVT_QU~md[ZXQ}ǸzqkĠ}tihdcejjghfdmnR75::98777775551/00*,,++-,++))**,,,*,/23432320,*+05666789::;<:;@A@ABCCCCEHJMNOPPPQSVVUUVVRNQRQSTPOT\[Y_`]bb@ %=dqejo`]hnmmmkjiopnnnnpngZOIDBEEFGHGIKNLOLD>@DH@CEC=:0*$ ,9;=979:99:>KK9%$+28?EC@?CGNZfYKFFGC>BHB5.26=IDXukJ5)/$" -Znf_XWOB7872;NRLILMKJJOMLLMPRPMNNQSUTRZZ]^^`ca]_^VNNRTQUWSNKHFFEEFGHHHOROU]xo2$ ###$"#((&%('++-).@G=9;EEDP\H.$!*JZVSQRQE4)&#$!!$$!$$$$$%%%))+/2=O^tdRZWTQPQTVRPQQLJJHBAGJQXUUUTVULa}k[egdaVLMYZZXVVX]ZYVUUZ`eeefjl[Uuҹ!!"""$*.<̩oD76H\úxkbSOSSmrh_\\SgŽ}iwÝyrokhggfhkhfckoV:4888887767542011--++,+))*,.0-+)+/001010003/,+.1228889::;:==<'+1/2<>D^ovxywspqstovvZ;7A?=@GOVajaQKMOMKRWPD@A=EUphT\YVSRSUVUPOPNMKEDAFKRVSTUTVTKa~kXbedcYPSMOW[WY^\]YVVWYahggfej]Tlxtyzzuyh!#%%$&).##Y|Z<>EUqoxyjic]WTdype\]XXúuiηtoekmhdglptpjpt[<3678888666311252+0+*-+*,.+,+,//./,.0210-+210013559;=<:9;=<>=>AA?ACDDEGJNROQRRRSTTXWUVSQRUNQQPQOMPY[Z[]]_eS'/Yrfdjd[gnoppnmkonmoqpqocXPHBBEGECEILKKMMHC@BDCFB@B@>AGOUIDV]W[cNG[[F=>53..3204-33387/*,)$!#5779<;78;85MLKKKJJLKRSWZPPebba`][^b^[[]\\]\^^b^SPTTTUUSRSPKPLGFHKLMMKL`ly{V$!#!"!###$'''&''*(%(@[\C>76BD@LM;6-,HUINXS@."!+.-(#"##! !#%!((&')-5?BYylW][YVUUVVXOKNQSNC?=CIPVSUTTVSJblV`ddcYSYTQY\V[b\V]YMKYcdfgdch]Rh~tu{}}rV#'*'$'&&# @fAEEKf~{ywvtwuomnmhkgb`_]ZWUMR\ZWY\`[[VVo{qj]^\Sk}mvչtkdmogcgkirngms]=156789765:40/011,-*,/.-/.,,*+22-,0010/-,++.00/16;:=?>;:;>:=>?AA@BEDHLMKLORTUSSTUTUUUTSQQRQTRPQPORW[[X[^_dZ+ +Qnlfga\hononkioqnkmnmnof_UGCFEEDEEGIKKKLKEAACCB>>AB@DLRaR?GRT][ECZX=9C>ADIKC;DIINWaf]OMQSV`t|pv~yl7''"  >eja`UJ==@IG40BTVWTQQSTV]]_^LGZebbb`\\_\XZ^^]\ZXW]\RPTRSVVSRSSQQMIIKLKIPMM_o~yH "# !""$&%&((&%*($*Hd]H?<47BBFNB1+KTNQUF-"!"# &1:<72/+*)'%"! '%()'*,2APbzoY^]\ZYWVVWOLORSOF<=CGOXVUTUVRIblU_ccaWT][Y]^Y]a[Y\\XWZafeedei[Rm{||`\ %*-($&$$#-azqePCM]jypqqqnjfcbccc`\XVUUVWXZ[[RVUONQPKJKLKJKOTJNXZTRUROOTSXvvlk___UWrmίrkkedklfejsogls]=====<<7<>?AAADECFLNLNTNQRQQSTSQUWWVUROORPMNMMPU[[VY^^d^."*Jkpid^\hppqqnmkonjjlnqmjh\HEICCDFFFGJLINOIBADFC??BA>BKSXTJDBHSP;B_\KPVY``XLEQfsiVLQSLFJDD@775*&&$ !,563:JG/$/84055:IOFFECA?>=;26:;A@BJPV]cVLMQQR\qpjwzp{k3'&" AgkbTEFA>=FJ:3APVZWUX]`Z^cf_NJVb__ceb``a[[__^^^[Y^^USWUPUVROPSTTNGDFIJJNHBTlw;$$ !!%&$%)(%$+)$+Nh[K>C;2=EHJF-+MRRTM7 %/316BLLIG>>=:5/)%%'$$%+-.4:D_y~|v~}yxqZ^^^][YWUSPQRPPPL=AGGNZXSTUVRIclXbec^SP[XY[[[_`[^XX^^Z]fcbeikXStwy[^a"$%)*''"$$ &?ZgVVVWWXYYX[SEG[jmpdRSb`UTQRSTTSRQFHLOPOLKOOOOONMLKJJLLKLMNTVPLMONVLOSUl~rtfTW]Tc{~yiv{qmlhhllhhlnqjks_?26554332253223111.-,,-.--//00.,,//.-,---,3213;CEB@@>=<<<<9?A??@ACFFHLNNQUTSQOQUWWSVXWVUTRRQPOONNNPYZW[^^^b>"Eklgmb_hrojlllqmjkkijlllbUMGDHDIIDFMOLOIKLD@DFE@AFD?DPX]\ZSBATS<O[NHJLMKF<5JL30:;;;BINU[[QEGNONWlwxsvxksc5%/" Fc^ODBFDACGC:=JZ\]YUX]^X\cbVKNX_cb`a`\Z][Z[^_]ZZ\`]USVUTRTVTUWUPJDEGHKNKMRUe|g3"" %&"'&'&'&'&&')'5XfTHRSA.-8>FE2/LNKN;&"*/:KQOKDAENVVUTSQLFA5,%%&$"#))-16BZomd_gomimwyww|~|{}xv|v^aed`\[ZWTSWUMORL@@AISTSVTVWNIdyfYacb_UQYXZYWX[`c\[`_XZbegfbblaUkz}|]RbW!#$)+'&#"! #1FTLLKLMOPQNW]WKELVcvoYW_ZORSSSQOMKGHKMNNMLRQONLLLMJKMPQPPPNRSOMOPNPJLOQbuqgYU\RWj{}xtpwulimjjmmjjmmpklsaB3444433331563/111/0110/..,,.0/-,-1/../00//5<@DEB=;<>?@?>=;??@AABFGHKOOOOQQSTTTUWWVUSRTWVSPQSTSPMKNY]XY\]`dD"=dlimc\erpkmlkljilmnnpnpk^QIFGFGHHHJLNLJLJA>CEFDDEDAGOXagibOENO?AS^^adeeinrz}ptu|Y:DGFEECCGB=950*''+*3?=0.7@?<@DABK@07<:98=DIS[^LBEJKLSdqxrccgewf7"&'!$$(ANJHGDBLIHHB=CPY[ZY[^\Y[^a_WPQU]ca]]]]^_^\ZYYXY[Z\[TQRQTRTUSTVTTNIIIIJLKOQTfv[+"" !%&#&'&'&'&'&(&&=_gXIU[M5-03@HQRPSTUVOIcygT^cc`TNU^XW[\[\^\[Z\]\`ifecdk]Sk~~{ysr}sW]VU!!#*,'$# "%->QJJKLPSWXW[_^VH=9@_tpeZTTXXXVTPMKKJHGIMRUPPOOQSVXJLOPQRRPMOOLORRMOONPRZwxmjd[]YY\fw~tmns}oihgeeggeegimikrcE42233444424530.143320.--..,*,--.0../////05>EGECA?>??@?><;===?BBCHHKNOPPQPQSTSSUVWRW[ZXVTRUTSQPOOONX\XX[^`gM!" 4\kileXaqrlmljljkmnmmloroaOGFEJFEIKIJMHJKE<<@AACGHFDEGKWafcRCCJFKXaddcbdhqywrszZABEFHGCDMJGEDA>=>FBAC@:>HFFEEHC?ATJGHHIKICMXD.3:;;;AFINSSJCEFHLTartcbxqwvX4%'&%3;9@CKTVSTTSUQJbyiZbdb_VR[`YU[a`^_]YW[_ZZcgddgiYQmvoqvvsnplhtu{[\WYo "*-'"# !'.AVPPNOPSVXXWTU\`UD4=UszhXW]]]\YVSQGFDDEIMOQQQQQQQQLNMKLOPONNLLPUSNMRPQTSj}kmoh^ad``o~~towwlfiffegffefdgegnbG51113344552011015//./010/.++-//..-./.,.48@FIEBABBCA?=<<=>>><>BDFJJMONNRTTQRRQSUUSWWUTSUTSTSRQOOMMPVYWY^_]iV( !.VkiieV^orlmmkmllnnljhrxxiTKJHMFDJMKIKNQOF@BDABBCDEEIMDKPRTPKLOPT[_`_]eeiu{pm{d8)1455634:A>=?@ADHGGILJGHLGMOMLKOYRFAEGHF@-9[bKDB<@AGMORUSNKLKNX`jpna_c[[YF0&##93/,,4ANSU_ipmaUJC4*"#').33K^U>9?CB?CGEDHJHJJJIJLMRRRPPQSTPSTRNNPQLNPPLLPRKNPQUXWSRKKNLOL@;>AIRTQQTPURJazjV`cb`UOW[YWZ]WWb`ZZ^][]bb_cij[Wt|~xv}yxysze]ZLd !*,'""!!&+:NMKJIKOSVTUSQV]]YH?AUmwlYRSSSROMKCDFHHGFEFHLNPONMRSOHHNRPOONLQVUOIQOOSM]okqrccecbo}rg`dccdbccdc^`^`f`I600123344110.1440210000.-/,*,.-,-0/..07@GEHHEBB@>@?=<<>@BABA@FIKMLOQOPRUTOQSTUVTQSUWWYYXTTRPMLLMNOUYWX^`]h]1+QkhfeU[lqjlmkklnoqpnlv}{hSLMMIHIJKJKKOROJNWXTRNLLLKNTUYXWZZXX`^]]^]_ce`_gh_neG813.1/,./--,+-00.-.-/3565567<<;@BDIC::@B=5+.+@JCCEB87;?AEJIJIIDDKMQgol`YTPPGDIJ<-$.FN@./FPJKOLGPLIB:?JOT\YV_^YZ`\[]\WVX]aa^`_ZYZYXXYZZZ\ZZ[XQPUWUWXUVXUPMMNLHGIJPO[p_5!!"#$$%&'&'&'&'&)%*6JbiZHO``M>3#!1&>L52OPLJLJLSZLBBB;7973-((,49;H\jlfdf[J6)"#)1->QPD<5915;6488:;<CDCACGHGKNRQNLPURPNPUUQNYUT]mvl]RDBIMPMC;?AGQTQQSOUSIa{kV`cb`UOWYX\[NEJTZ[\ZYZ`efbfjhZVo{tx~|tkqkflp|bgcP_{ !)+&#!!%,,4HRPMLLPTW[ROSSMOWQPPOP]kkbbcca_]\OLIFEFGIFHKORTUVSWTKKTWTQSRPQVUQLSRTXRYtxmnuriffjxtl`Z\]__]]__][[YY_^M911122223.//1231////.--.00,**++,/..05BB@DHJKNQSSRRQPQTVUUVVURUWVTTUUTQMJIKNQLT[YV[`_d`;(HhjecTXkqjklkegijlnliuzeTLJKDJMLIILMLNJIT`b_d`^`_]^bhhgeeedcca_``]]ce\WZWWqhRD=A@;9768:93//30..,**+'--,/1./1/7A@:1)05;73536--1669<:67<:BEKRNIKMFD@;BLMRZYW[\Y[^Z[_^WUX\_[WYZWVSVYYWVWX[\[[YSRVYWXYVWXVNLLLJGGJILO`mT-"!!$%$%'&'&'&'&'&%.33EVSJRcbNF>,#+%!184DQNNJABLRQG@@@>;;4542-)'&%(3FT^hrrhZJ8("$("*=>;:<>=;;:9;>><>B?CDCGIJLQZ_XOJ>>GKOLC=ACGPUTSQNVSGa}lZbdb_VR[ZY^TBGL?>OTQVYY`eaefdZVhqi_Y]cdagaWZt{d[[TVw!"')%$ *2,3LTQNKKNSV^ROX]Y[blpymJ>Yv|rf\VRQ`\UNJHIIJRTOQZ[TSWXSRTUSRVVY^ZZfuovzrnor~rkc\ZZ^aa^^aa^[ZVTZ^R>222211110..11/.0,-/.-,++---//-.105>@A?CBA?>===CGHHKKMQPQSTSPPPSUVTSUWWXXWUVVSOMNPQQONLLS[YU[a_`aD!"=engaRVkrjjkhjlllmonkuj]QGHDHLKKLMMOPIFOVVUZXUUW[_bc`]\]`dh^aaad`\^b\]`\]kz[JC>;=?GCFEDEEDFFDDEEBABDBCGE=873;<:<>==627@A>:41?KA8747018>?@A=;:>FQQQXZWUVZ][Y[__ZVUZ^\XYYXYY[[YVUWZUWUTVTQSYWXYVVXUKIIIHEGLIIObgH&# !%&$%('&'&'&'&#+5/"%6@@Nc_KJI6)*1'0CLLLKEAKSPOKD>=><8@;64651-+(&*4CQZfglpeK1"'(*$6sz|tsufbliXPV^RKLNHA=CE@;=:694;?>>BDE9;ELMPM@<@?BKRRRONWRFbkT^cc`TNUZ][H>SYTXVSVXW]^__\htqpuz{{|qR\hNOo "#'(%%$ )1'/Mbbdiq|T?Skffeb_ZVTX^hqvvtrkaRC:78:>KRPT\[QTZ[VRSTTQSTY^\XZvszuuwut|zzrhe][^]Z^^ZZ^^Z]ZURX_UB3322100/32.,,//.-,,-.0001//0/038;BIJGEHLHHE@>@A@CA><<>ACBEGJKIJPQPQSRPQUSTTTUWVTUWXVTSRPLNPRRQONNRXXV]b\\`I!#4bri`QUktljjehiihjmli~z]SKELHFFINQNKNOIELQRSQTSPPSRNRQTWURQQOSQORPLLKFHGCFHB=?@=HQHAJLLKIDCJFDEGHFFHHDEJG=67=A>=GNNMK>8;=><607LNB>7154685563:6635=?BEOPLMOOQXUXR9$ %.;826O]UKGLUK<799DOOQTWXX[ZVYY[^_]WQRZ]ZYWVWTUWXXYYZVXTSWVQPXVWXUUWTQOMLHEGLJFOca? $ !&'$$)&'&'&'&'&/3.'$(02F`\GJO>+;A'9NNJLFBFGJPTJBA?;;@85236886;91&#+6;NKThulTA% &',U~}tokqgN>GD=77;946?JKHPQE>BABLTUTNOXREckYacb_UQYW_YD9?JOL[a__cdf\Z]YYZUZ``gv_TejTO`~t %#""$)"/65AUpjb]UHAEJEBGK@558.4?LTUPKD>758=><>KVXVWWUPTVUW[YRRSTTSRQP]}y~ttvy{|slkb]d_QXX\Y_c\^RWWQSa[C63+,52*/-11--00-/.'050867424;@CHMLNNHHJIGFHF?>BABA;9?BACCDFILMNNPQQRSSSRSSTUVVWYVSSUURONNPQPPPOMLRXWVX[[bhO$!2Wmh`VReqliihflojhklj}{`EFGAAKNNJIKLKPNJGNX[XQVZZXVVXTUXXURPLRONLJKJFF=9999::AGMLJNPB87855873JbRDF;9;;:;?A@C=;<>EJJMZ[]XR\\UQXQ5%%!! )' 5[`PJLTUK>98>JNNMMSVSV[YWW[ZTTUPTZ]ZXXXUWXWVWXWUXWVWYXTQTTVWUTUTMIIKIEFKJFVdO-!" !$%$$'$$&(&##'&/.'&*+-9J_\NOH5<9EPZ[YZYWSWYZ]a_YWWXXWVTSWizustzztkg`S[g^ccgbafelcee``kjY=3,-3872/.-,+,.16BIW[X_^SJEKPOPUMKMMHGIHHFCABA?>?==>>BEFEHKNOPQRQONQUVQMRTUVWWVUVSOLNRROORTRPOLJOTWVUW[]]fV,!.QljaOSfngjmkdgjkkmprl\KCA?@FEIKJJNPPOOKGHNPNPQPOQVWUXYZZXXXXWXVSRSRPQMLLFEO[bjkr~uYGPRMILKIMPJGJLKJJJHFFB>=@?ACHLJGKI=578657:6G\SJOI=>>?DJKHAAEIEEKP[hqkb_]]Thw_0%% "!5Y\NJOTSG?>; ,LSKLNHDDDITKHFCA@>;>>?@;8;A@CO\]UB..,(-B]mohV8*)'Aqwnnsronlmlpvslnxvt~yw~}v|x{}UGPONLFAACKNLIJMJD;@BEOTSQQQUNHfiW`cdaTNVVWSE88:59BA==>?><>A?@CDGGEHJLMMOQTUSPQTVVUY[YTSVVSQPPSSSSUSRPMLOPOMSWYXY[^Ycc@">dpcOQdnjhggjedhihjobMGIBBHEEHJKLNOPMOMHDDDCEFJKFBISNOPUXQFBFDIMOVZT]fg_WVapt|v^LAFFFILKHIDFJLKGC@B??CDB?>@BDIMJHKD:0---/20'1SYBIUULIPUUUVTUUUVVTPUVY[[YXY\WTUXWTRYWUUVVUTWVWXUTRNJIIJIFFIGNaZ2"%$##&'&')(''(%#$(22.*(# #-M^_LFSE5C>#2RNPMNJDCGJLPHCFF?>HLLMNMKMRRPPMHKNKJH@3')9J\hnfJ*3Zjikjgkqpqssrtwuqtz{wy|wzsqqrpsszpTGRNMPOKKOOQQMLPMB>@?CMRRTQMQOId|hUde]ZVRTVSPI?>@?98666:=??==A@A?>?DHIFHKJDF^|vtldx"%!"  #'6>;9:788BDBQ\PGY_[U?(!%(*$"(&$!$)))*(%')'&'-1004997?@CFGKNKHKH@8455799:>NUG>?AEGDB@>;=;>?>?BADLPW[[ef]d`B% &&"?[ZQX_]QABID;?ISXNIOVUUWVTVWUWWQTWY[[[ZYSVYXWVUUWUTSTTTSUSUWVUSOJIJJHFFHEP_P*##%#$''&')(&&'%#%*0.+*)$$,-4F_TGTL887$3QOMPOMHBBJPKNKE@95;A?ACDCFKLHKNLJKJNPOG:016>O\ikI.7SONOQWZVWXZ[\\\\_dfceikhkrvqnrtpmorxxiVNQOPRQLJKJLPMIMMD>?>CMQQTQOSNFa|lY_`a^QMXRNNKD@BA<==::;AEHEDEG9:axncYQIDBEGHOdqvodv\$%###!!%3:5..39?B@?EFFIIINLKJJMPSTNRVVTSUXTUTPOPPNORTUVWTPRQONOQOLNQUXWX\b`adL .Xn`HUikfggghiihhkmmXHADEBDHDFIMNMOQQNKIIIFDHJJHHJHCMW\[TGBMQ@BPRUUJ^zUIJNIJKDFJKGCDHKHECFGFGIPRRTVQLOOMJIIJIHMKBCC833-4::7777448=?EJIKRQ[[Vflo|mB###C_]U[a]PCBHD=AHPVPKPWVSUYUWWSVYSPU[]\[[ZUXZVSTVWTVWVTSTUUSUWVURMMLJHEDEGCT\C!#$'%%''%$&'%%&&%(-0*$&(#%06%-RUEMPA2)$9SOINRL>>PP>7MN>::4/1//3457:;56;9:?A@CEFC=3+-9BK[f\KNIJNNOPLNOPSROOSQRSTRQRUPRVVV]^W``_``YSTQOMKJJIGEFMPJKKD;=>FPQOQRQVOEboV^bc`SLUTOOSMFB??AA@=?DIIJHEL??r~xoe`[]gx~cmxiy]Y"#!$$"" *---'-@LHFFGILMU_F&%& &#(,+(&!&&)(#$''(%%(/3310.1421>PPLKNNLMPSPQVXTQQPQSUWXXWQX]WTf{}{{{wutppqniihaYmu^t̯i\NKPMKMMJJFDFDAADBBBBCCBBFHHHHKONNNOOOQRTTUVXXVTRQONNOQRSUVUVWURMOPOPQQPPRUWWVZ__`eT)*Rk]NZjhadfhgjkjijll[IAFEBEIDEINOMNQRNKJKKKJKIIIFDHOU\]XPA?NSDBNUPJH`ȣ^FUNLVRUXYVVZ]]YXYZXUTVVWWXZTORQQQPQPNLKF;95*)+-4;<===;A?CGFGKKJSWUXboswfM7(!#Da`V[`[NCCIE>BHNTRLMSTRTVVYYVVVRSV[^]YXXXVTSTVWWTVXXVTTVVTUWWUQKHIJIHGFFGWS3$%'%%'&$$&%$$&&&*0/(#%% &8E3(8IHGLH4#ARNKMQB(/QR14KJ97;5.-**,.-.051354574232/39817,/7;IYY>:>EDCFIKKMONLLNONPRPLNSUQSSQSSJKOQSVQNSVSNOYaZMKDHLHHKF<<>FPQORQRVNFekRae_\VOQUPSWQGCBGD@<;?FKMOOFLD>f|smpz|wmlsvljv~feO[ "# $-*(*#(@SHDP[XRVO+9D2)53,-0,*,-&'+)"#)*&,48854361145:Phkecghfgknjlruropmnqtvwxx|z{|}{tngbchfabd`\[ZQ[pqgyǽl[OJKJMOIJKFDGE@ADFCADDBEIGIJGILKLORRQPQRTUUUUTTSONOPQRSUTTTUSPQTMQSOLMPRPRWZXUW[\_gZ/(Oi\OWeifeeidgjjiikmXGADADJIFFJOOLLPQNKLMOPQPQQMHGKOLQPONDCQRHBNYNELgwbiiehicddbdeb\[XVSNJLPOQQTVQMOKKIGEEDB8=86433.39>ACDA=@?DF@BTeSNTRYd_aZ[N7%$ "DaaVZ]YLCCJF?AGNUTLHNRRVVYYVUTTUWUW\^ZY[XVVXXUSRSTUVUTSRSQSVWWSMLLKHEEEFMXJ'&$$"#&'%&($##%&',2*'&)'"0IMH.!L\ih\V]\RNRRLBIC>CB@GA63985796789;;;:==@DC@CJJEINMQSLJOOQVSR[\YWc{sWKBFOLJIB>=?;76637?@>?==A:8CECIJIMV`b[S@?DJMOJ@9=@FPSPPPPQKHdxcU```]QNTVOQH?bZQ`jieccghhhkigkhWJGFEHGFKHHMRSOLMNKIMONMOQQRUWZ`^`bcgnrrty~z{xpnpnijje]WWTQRQPONMLLKJIJLNMLMKKKIFGJMEDNSOKMJKLMMKJH=93/-,*)').6989<@DJOPNLLPQX_]\hzlE% "I`[TZ]VHCCHH?=ELQRSOKRWTUVXWUVVTUTX[ZY[[YZZYWVVVTTSSTSRPRPPTSNJJKMJFDEEFNK3! &$&$#%&&'(($$&'.2-"#("!>YWDR`D#8PGJ:!%FOIVM18@5FWC/88422.-1//641Fbsyz}~~}ryzjemk\W`cXOPRLEEE>=>;89:<86:@B?;69AHKMI@8;>EPTRSRRSMJfzdXbaa]OLQRUVD7FQOPPQQQRUWU[[[VLPdmdYV]ebZ[aZ`pijώQ__Na #"!""!)2+%-#4r]tH"2TF3/563521/-,,,,*)'$%)06987544442230J~v±bHFHGIGFHIDADECB@ADGIIHGJJLPPNOQSUUVVVURONMKMPSSRRUUQRUUVTVXTSTQPQRQOMNPNPTWVSY``X^bG8[ZTdmidcdfeffihgjfTHGFFGDCEDDHPUSNKNMLPTUVUWVVWUUY^`abdghfeeffa\Z\]YVWYXXY\]\XX[\YWVVUTTSSTUXZ[WRNTQNLHEGKQCBS^WOOPOMID?;820....--.-3@EB@B>EHGHLMISRW^`^epjO6(! $G_]U[[VLFEGF@@HMOQXSFLWTSUYYWWVRUSX^]ZZ[\\\[ZXVTWVUUTSRPPQSTTSSSNLIFFHIINA) !$$#%&$%&()'%$$(01)%&"1VfXCOfY((JID>,1LQKUA.=6RlP8:6<21./2127:6G]k{yqoqk`htrgn{wim{~uu}|{{oo{d@;B>977998;>DGHLMG<>?DMQPQPPRKHdxbV```]PNTQSS?7ITQ[]``^[XWYVTVWVY^pw}}̂W_]L[~!"!! $.5///>a{+WM2-4026,********)&$'-6;>:86424567706_~yvvxry¹QEHKGHGGDFDBDEBAEDDFJMLLLJMSSPRWUUUTSRPONNMOQTSRUSPQTQPURNQSORURQQQPOONONORVWWY]_X\eP%2S\WdjhghfdcdehghjeQGFD>;5406=?DPA?OYRHFCCBA?<989876530///4<>:79)2JQQS8'@@PnlM83=3101212697DXct~xyjcc``dbbafg`bkm\^kpffpsslnwunp{|oszwD8C@=9779:FGIHGMNG;<=BLQRTOOQKGcv`T__`]QOUWTRB5:BFPSX[^`bdheff__^X[fw^[bMMr!! ! (:/#.3 EhACF-34220+(()*++*('$'-4;=<:87533467:26S~~xx{||~|wqlnnqgCEHFFECDBFFCEDDDGHJIJKPSMPSSRSTTVUSQNMLLLOSTTSTUSSRRUTPQTSVURVZTQQQPPNOOLOTWYWXZ^Y\dX-+L]]aeilhdefggiijkdQHG?3*#"!(17AJPPNPKB?>@DADB@EHJKKNTWY\afhmoorurkjlcWZgg[^_\URPMHLLKIHGFECBA?ACGIHFFFDBCFKDCMVSMJEGIJIEA>973/,*++)-268940*++***-./6CLPOPPQRMIdv^Vaaa]PMSONMA762259=ACDFGJOVTJN]eillkjkf_[accb[X^dYNL`d!"215/+",hw]j/7>0-/0,'*+$'&$$%'*,:;=;9789777765327Ad||{|zyxzzxvonquutw|wxyyyxxx{vwy}ytvtonprq^^d^ZVhLFHC@CFEBDGEBEGHIKJJLPQPNVPNQQMMQNMNPPPRVWSRSUSRQUTTSQUWSRTVUUVURQQQPONOOPRUXYYXW^`[^_<!;UfiheecbcaddfgjjaN>70/5:>?91+).;GMLFBDFEEFDCGLHFJLJJMMJHIMGHQSOPWV^^VVbkkpsspnnkgdcb`_]\[VVUQMKKLIFDB?=?CHKNOMF?:531/.../147899;<:@JSWWWYYXY[\]aeddefkrx{uN%#!.Tc^Z_YPHDFIFBFMLIQUNFIRVUVXXVWXVSTY]\[ZYWXYYXXYZYXWVVTRQVVVTPMMNLFGFCFID1! !"&##)&%%&%%%%%$*1*#%,")')-+&%()&$#'.6;=:8788658999740.Lm{|{|}{wx|y{|}||}~}ytuxyncf]^]cRCA@@ACFBEHFFJLJIIKLMLMNPONMNOPOMNNPQQOPTSRSUSPORTQOSXTQTTTSSUTTUSPNOQQOLPTXYWVXZ^bZ\`@!5Plgcee``hfhghhji^I:68>EEEDDB;.',7LKC;;<<>BB?@DFFHKJLOOOU]`dmx~tvslfb\USRPNKIGFACEDB@ABC?:8547=><97545712468:;<>?@@@BFIHIQ^da_bbcda]^djlmkgfkqtwP."!!/We]Z`YOEAFJGBFLLHNWQCHUVTVXXVWXVUSW\[XY[\YWWYZYWUTTTUUTSRNPUSKIMIFGFCIH=-!$$%!%(#!$&$$%%$%-1' '9KH@NULIC:+,75* .J:,01;==<<85:=9844ALHDFJDHMJILJLMPQMMONRPJFMROJFMPORUTRWUZ`]_fjfmhentoknoqqpptxrttpmsskgoqwzcHEEEGIJIFCFMQMJNJ=7;>EOQNNPQSNJcu]Wbaa]PMRUUTJB>9<6654358:>;:<=<<@?<>EJLOTWZVW_\TUJGMftebw " !+<3-,2)6uk_rgA4,&*(%$$#$&)''+39<:==<=<?>@GE>CNMJKPPIJOOMHLPLLOOLLJJNNLMNQPPTURQVVY^a``cljkonklqoutswnUBNOKGJIINHHKNNOKA<<=DMNMPPMPKJgu^Raa_ZNMRRPRNF?8:<;9524;BCBDC;6@MWPJIHEDE?@CFHHGECUnud\{!$0:50.2-!%6IXe`S:$''()(%$%($)18;<:9;;;:987676;;/2Y~{y{zxwurqnkkostwy}~~}yuszysqtÛfQCA>>BBGGHHIJJJKMNNLMPSNQSQONPQQRSSQQRSQPQQPORUUUUTSTVWXUTSQSTSSSRPOOONORVXXXYZ[^\adN'.Rnrh`acc\bfgfdllY@>FIFIHDGQRNJ7'-HJFHNLIFMMLMQSSU[XTSSUUTVUSSQMIFLKKLKIGHGEDFEBBE?ABBBCCBEDCDEFDBHGDCBCCDDDGGBBEECEFHHDBDCDFFFFGHFDM\```_```aabeikkkjgirzoA# " 5\dYX]SJIECFGACKMKLQMACOUPUXYXXWUSUXYVXYWYYXXWWWWWUSTUTRRSSSSPLKKJHFEFI@/"!"$$##$##$&&&$#%%//)/:FLNF?CFB>AB=-$.6/!&)(,/787@CD?;8/.982266215759<9<@?ADBBEFEFIKKLMMMOLNOPRSQPQQQRQOMMNNOQQQRTTWXXXVOLKMHEHGCBGGIJJKI@9;>FOPLMPMPJJgu^U`^][QMPPPVTKA8:888768>DADFKH8Bf{pfdd_WRTNGCFLQS_qt\S{€#&7:66761,$2LQXX7!*%)'! &&!/5;<:778<:7558;==78=Kn}zspqtuu|}ʲpUE>AFFEFGHIJKLLLMNOOPPNRUSQRRQQRTTTSUVRPPRTSQOSTTTTSTUUUTRQSVUWRNORROLQTXYXWXY\_]]dV0)Mpre[]`dc`eggfljT==?@A5&&/1'!! ##(+.114@B=>;<:69932<916844668:=?AAABFIKKLMJLMNNNNLSSSQQQQONMPPMMRTONGFT\WOGEGGGJH@9;>FPOLMOMPJIhv]U`^][QMPRRVOE=9=<=;879>BBCDKI><978968:;;9756=Tqymllsz~||ʽ`B?EDBCDFGIJKJJKNQQMJMQSSRRRQVUTTTTSRPRUVURQPRSTTSTUVSTTTTVVUSQQRQOPSQTWYXWWY\`][c`;$Ffia`b^bjc_aihgdV>=@?>5&!+)&($!&(*--7AKJ2 &$%).;RZO5+Dah|yqvwkbhh]R^]OGJIC=::=;>>;647;746678;=?@@@CEEEHMLJQOQSQUWSZ[W`v~lZDCFHIKIA;<OixqmlkjmrqmhghjkjladΠp#"0<<787585,&%$IvW#$)(*(&*5AFB@=<=;8587555689So|uw{rtvmmpkflqkheeffc`achpzWCBB@ABCFGHIIJLNPOLJNORTTRRSUTSSTTTSOQSSSSTTSTTSSSUWVUUVYWUSSQQRQOORORVWXWXY]`^ZcfE >Yffghbbif^_ijfdX@<@CEIGHNHKLSK>LcQABFDGFABA@A@@ABCCCCCCDDEEDFGFCHFFFFDDEIFGIJGFFGIIGGGHGJHHIKKJHKJIFDCDEEEBBGFDCAEEDFGEBEDCBDFHIGBFTbc_][_ccfhhdhgknhcod2"!$C\^UTVQQNHDHKDCKOOKJHHKOPQQRTVVVVVTSRSVYWSTUVVUTSRPQSUTRQNQQLIIIGFCBDE;,"!$#!"&'%&')(&%%%#4QYOA@A?>=0#"*! /1& %'%)(.AWV;! %%';Yg[7Ah|wsqwsgdih]VZ\QIKKDA@CA;8;<:88;<:7?<::>DFBEGFNbfXI>?EJKMI?<<=CMNMPNMQHHhv[Raa_ZNMRNTR=5BKNFKMKKMLJFDKOG>J\d]VSSU]fec`]\\^`[~qX#!)7::6:6244/-!#V[!$8#&+9WsviaZPKIHDA@=;>@CEGMMJOQPI?D_TGFGEFFCFGEDBCFHGEEEEFFGHFDDGFDDCCEEEFHFEDFGGFEHHFFHKKIJJJJJJIHJJIGDDFHFBBEFHHDBEDCFHHGDDEFGGECECES`b^_\^aegigdiikmihs^/#&HbaVTRNOKGCHLEBHKPPONLLORSRRTTSRRQRTURUVUUUTSRRSSSSTUUTSTOPOKHHHHEDBEB5&"###!"%''()('$$'*BtrL?AAJE==A>:=DA?;8;3"&%&0/,#!'$*3B\[; "*,(9YlZWg|uy||}zpowtgckd[UVVOIHMH<9><798857>A?A:=CIJKF<9;>FOPLMMMQGGhv[U`^][QMPPUP:4HSWX^`][ZUNKNNRRFPrwttstvpomihp~ӌY^! !/8899798448:#,oZ"$<,+Fx~}}~vvx|zNFICGLJHKMKNPRTVY[cgmqsrpnsuu~~gLDDEEFGGGJLNLJILOONOQRSTTQRSSRRSTRVXWUUUUVWWVVUUVUVTTWVTURSRONNNMNQVWXXYZ^_`^dkQ& -PjkejiaYbddffibI9C@=98>=1" 1@A%-,/EXX6'#+%+?Qfnhq~y~}~{jlt[???<8:<417=?DHIJE;9;>FPOLMLNQGFhvZU`^][QMPSTM84AJLV`impsnfZ_[[\U]wΐYQe"'156;8+1<7366#1\$!/)W}u|{~oyIIWKIQRRVYRVZ\\^bfafnv~bthOHHHHHHHHIIJJJJJJLNNMORSRRQQRSTTSSSRQRVWURSUVUTTTTWVTUTQRRSQOOQQOOSVXXXYZ^^`_dkS* (Wg_Ybea^`bcede[@6=EFHKGHIPNJJGB=DOMJKHEEGJJIIHHJHFDEGHFCGDCEGGGIIGFGFEFHGFFFEDFIGHIIIJHEJIIIJJIHHFEEDC@>?BBABAADECBEGDBDCEGFDB@@CCEQ_`]a_]^eihghgiiggmu{U,!"  .R[ZUXUQQJDGOMBBKOOQQQRRPQOMNRTSQPQPQRRUURUTRQRSUVWTSTUTSSUQOMIDDGEFEE>,!%%! #$$&+('%&(*)(3kM;::;?<;B1  8F=$ #!(58BZM4#%,,;Yqpr|v@8D917?95=CCFHHIF<<<=DMNMPLNQFFhvZRaa_ZNMRPOK<4866FLPQZgnmlopkdb^Z_bkx̐WYfT$-/-/2-..58464,>m-3Dd~~ymuvusQIPQSRRRVXXUUXYXY^ahr{uwwrvoSkDZwOKMKFFIF?BEHJKKLMNLNNKMSTQQTURSWWUWVSRSUUSUSRTSQRVUSSVUVXUSPNOQPMLNTXWXZYZ]`^Zf^.)Ne][b^bc`babg`K98EGDHJHJOSOJLI?9EONLMKIGGHHIIIIIIFFGGDCEEFGHHGFECEGGFEEEFFFIKHDDGIJJIIIJHFIMJFHKGGHFBBEF@CDDEEDABBDGGGEBAACECDGFDFDK]b^`_^`effjonefmcfxsG& # $ =[]VXZTQPHCFOPFBINONQSQRRPQSUTRQRTSRRTUSTWPRUUSPOOUQPSTSSVOQNHDEGFDCFA/""# "%&&'),&!-'#)%)C~Y4>6<@?=A@=?>==8:<:A9" 3HA5! #$ +=FLC+%#!-6HclovB5@???=9:89COOMQLOSEGktXV_^^ZLJPOPKC=:859;>?@ELRV[SMXgmnnj^YadbgvlW[bZK(-+(,0/-,48455.3Z@hxy|}yyukktzuFCOOOLIHJLNJJNPQUZijkhfgkpdebkor{uu~WKLF;35=DLIGGIKMMJIKLJMRSQRRTUVUVWWWWVUSSVRPPRTTSVVSRTUUVTSPNORQNNRVXXWWXZ]^\Ye`5%Ea_\^[`a]abbaVC7CKHCEFFKJPMHIJA9BMKHIHIJIJKJGFFFGEEGGEEFDDDEFGHHGGGGFFGHGFDEHGEFHIIJJJIIJGGIHHJJHHIGCBCBCBBBAAABA@BEDCDEDDB@@ADFDFCK[a]__aehhghiogiofj{tC% # &!#Fa[WXWRRPGDGQTGAJSNNRSQQSQQSUUUUVXVSQTVVVVSSTUUTRQRUUQPSUSQOKHGHFDDDE>,!"#!$%&(*-(")*)\aC><=;B?>:;<9A:$!"0F?:5 #'/9GI;(! ,-:XkrvA5??@>:9AKIHHFFKG;99:BMQNOJMSGJnrUV`^^ZLJPNOH=9<:4679;=@ABCACDCKYb`fffifbea`_bfhge`Z]hfXRV$.-'%)/2-*38456,FQt{qosw{yvw~ǫ|tsqicqyGAEFGFEFIKMKNTYZ^chlppoqy}{}_GGE>3.5>@@ACFGGFKKNNLJKJMQQQUTRSTPNPQOOPQORSQRUTRTUTVVTRQRRQRQNJKRTUXVTW[\\ZWdc?!<^e`^[`a^acc`RB?@BGDFDIX^]^_cghhiihmgiogm{o<# ! !% .SfYYYTPSQFEFQTH@JTPPQRPQTTRSTUVWXXSRSTUSTUUUVUUTSRVTRQSTSPPMJJJGEDEFD7' "$$%&&&&'((%'),$%Eyk7:C2).<=$ /1%'35?A6*$%(#.PmwwD;E=7;CFHIIKLGFJE58;>9;8558;<;B?BA9=EBBJPU]][]fjnnkiiknie\IDd*3-'&(,4-)39567%V~dc~tostspmntz㿡zpqpjao|TDKMOPQTW[_^afgdceorvwvwy{xsw{tsvmp¯{dJFFHE=9::>BEFFGHHHIKIIIHKOQQRQQTRPMNNNNOPNNPQRRPVVSRSTSSTSPMLNMKKPSTUTSU[[YWVadI3Yfb_^`^___a`TFB@DCEGGHMOONMKF>:@KLJKHFEGHHHFFGHFFFFFFFFDEFGGFDCEEEFHGECIHGHIGEGHHHIJJIHEGJJFGHGGEFGEEEB>BB??CC@ACCA@EFBCEDCCBBDDFCGT]\]`dgfgkmljeglfmxf4!  $!>afX[YQOTQFFGORFBJQQOPRQSTRSSRSTUUUTUVWWVUUQSTTRSTVTUSOQWUNMLLMIDCFEF@0""%$%%&''()&('&*"-[q43E:3('4<=A<548=;7C="&/"9?B8"(8.%-6<4$ -,5?<-###+QqzuD>B==CHHHIBGIGDIG=9;;BMQNNONOBHotVWb`^YMKOMSRH>>??@<87:=??>=?>86@MGD@AFIFEFP[a``di_ZNBEc.2,('&'3/,59677-BSl}wso{umebbef¥xrssk_kzXFTUVTRRTVRPQVXX[_voga^\[YYUYXPMKFhíyiQIFKNJED:=ADDCCBEEDDFHJKJKNOLMPQNPRQRRSSUTRQTTTTQQQRUTSSQRQOOONKOQVWTSSRZZVUU]cQ$*M`^]]\W^\ZZZOFDEEEEGGHKKNOLJHC<@MNLKHGHIGFEGGGGGGGFFEEDCDFHHHGGGGGHIIGEFGFHIHGJGHIIIHIIFFIJGEGHJGGFDDCADAADEBBFD@ACBACEDDEDBBCBCEDEQ[^]`ehggklkifhkfou\,! ! &#Pk`Y]YPPTPHGJRPECLOPNOSTUSNSRQQRSTTWSPQVWTORSTSRQSTPTURQSSRLKLLIDCFED:*"%%%%&'(((''%&'"1  /WsyqB>FA@FKJHGBEIFABEB<9:CONLOLMQELpsSVca]XMKNKOQME>>@===>@AAAC@?D=,IwcUMHCBAABFGGFGIKUt .0)(&"#0117:797:.Cyibdgvoe_]^^^~{wzvk]iuwMCLMLJFEFHGDDJQXaiaXMHHIHE==B?=>:>¬yiOKHJMMMMBBCDFGGEDFEGKMKMMKMNMORPPSTSQTUUTVTRSRQURQOPSTTWTTQNNQSSNOTWRRTRYYUUTY`U)%E[^]_ZU]]XSOIDFEGIJKKJIIPQJEIE9:ILJHEFHJGEEGHGFGHHFEFEDFFFFGGHHHGFFGGGGEFEFGGGLFHIJHHHJJGGHFGHHHGGGDCDBEA@BB?@DAACEA>@B@>AB@BFEBDCEN[`_bfjkjigejjklhqsS&!"""&.bpZ[_XPRTPLHOWRCCLPSOMOPSTQQQQQRSUVYVRQSWVSVVTSRQQQRQRTSQQSNKHIIGEDE@2% "%('&&'('&*$#)()PqB6<;87-/=+ $2;4,83$8/+A>&9B% .IP>-&'35(")5WrvtFFOD=CKMIEIIKH?>@?<99DONKOMNQEInsUVca\WMJLMLNQNHDDA@??@ACDKLGIE4Qrne\USUXRf!./))% #-46::9;7.2f~z~}|~vaX^bkhdaabccǥ~~xh`l|rtE>EGGFEFIKLIIMQTY`OKHHJKJGINSNV[Vcwp`JJIGHLNMNJDACGKMEHHIOPNOPPQRSTUTYXWUTTVVXVTUWWURVWVVXVTSSTUSRRRQJMRTSRSSWXTUTV]V0#@Z^^^YWYXPGFDCEAEJLNPOJLPSMFGD9=IKJJHHHLJGFFFFEFHGEEFFDGGFFFFFFFFFFEEFGGHHHHEDHGHIIIHIIIHHGFHIGEEHHEDDCCCCBABBA?BDA>@DC?ADEDBA?ACCDM\cabeimkgedjkkkgqnJ#"$""% ?poX^_XRUTOPKPXSDAKPRONMLOSTRRRRQQTVTUURPQSSSSSSSSSSSSRQSURNNLIFFGGDE<+!"!!%%$$&)++*)$'+);il:2A@13<1 ",+$3;.",)$76$)@@'%A>! )CWTF3/2)$.;9Tq{uJLIFFHHFHLGHJGDFE><:;CNOLNMORCGltXUc`ZULIKMLNQSQOMMICABDEDBFDEE>V#02,+%!&*5:<;:<7/L~jbgihiiigffgѭxeepvr}G=BDEEEFILONQVWSRSVWWY[]^__edYabXjicUNLE?BKOMROJDABFIONGDGIHKJOQQSQPSTSSVYYYZZWXXUWXWWZZY[\ZXUWVSQQPNLRTTVSQTVWTUTSZV5:V\ZYUVQNB:>BA?BEHHJPQLNMPQKGE@FLGEIJIGHGFFFHIJEGGEEGGEBCEGGGFEEGIIHGGGBEHJLIGJHGGHIJIGEILIEHIFEFJJEBBA?>@CDA@ABBDEBABA?CCCECBD@CCDL\ecccfjjggjhiihdniB!#%" "$%LxnX``WSWSNRNOTREAIMLMRRNNQRSTTRONQSRUVUSSROPRSTRRSUTSSSSRPOKMKECEGGE9' #"!%'%$&)*)(&',*)L}e>4999:>=, .!&6;*#" %59))>=!$F?! %9S\UG=+(;J=QtpDGGCCJNKHFGIKHGMI;:<ADFBFHILLLOOMLKKID@FIIIJHEGGEGIEDFFDFDDFFEFFFDBDHIHBEGGGHGDCDIJEBFIFFHIHFFGIHIKJGFFGGGFEDCC@?@BDDA>ABDDBCDCCACEA@CDECDEIV``fhgiookjhijegr]. # $)3iwcXd`SQROPNJQSLECHPMNOOPPPPPRRONPRSTQUUPRVTVRPQSSSSSQQSTRPOIJKJGDDF?/$  %&&%$%'('&",-"/cS936;96;;5/$,067&"",130/<<$9. ##3HTUXE4KS;Pw~sLGG@>??@>8=A@<=@?:9;>K`}~ݽbOTVSVXVVXYXWY[[ZY\\\\\\\\^]_Za^ZzRGJIFFJJIJMRPPTVSOMFJJJOUSKGIJIFEFGGJJGFHJIOQSUVVUTY_a^\\ZW[UNKKLKJJPWZVRRUVUPMNLOY?!,GWWZ__mbG5;A> ".82*" 5JS\QCRN;Os~oKJUOMONONGJIECGKE:<<;BLNLLJPVFInqNR__^XJJRSJHMPRRRPSSQSZ^^_^[\UEY!/5474.,+168::60RN?DD@AC@CB@BAACDCDDHUacfjihjgdgijhksf@"#"'#/Z{^SnjYUXURNPNMMJDFNQNNQROOSRQRSSSSTVWTTWRPWWTSSSQQRURPRSQNLJIGDCBA@3(""!#&&('&')*)(,/(+NzvH449?:6<=:9,/=3 ($%-374/63,AICGF4#""!0DWVINI?<?CDBCCA@DCDDGTaeekmllhehjjdjs]3#" #&";BKKINLJKEPrqRYb\Z^ZTOMNPPHEGIJJHDFOZ`^`^`WCS$0436622++/5:71,7DD:9:7554?==aeOWXTUWTUWTWZYXXZ[YYYYYYYYX\Z[]QbdJLMKMNNOONMNNOOQRRQRWXVVUOFFC??BGKMMMNOPPSVRRRPNKHFIFDFFDCCBDFDA>==8::7:AGHLMKMRTZfc9 JjbZ]T=3>648;BMOKJOQTRPLLLJLKIIGFGIGFDDGIEBEFEFEEIGDBDDCDEFEEEDDEEHFEFIHFEGFFGHGGHGEFGGFHJIHGEDA@?@?>>?AABAAA??CD@>@DDBCDACCEDFSbffjjhhghmjibgoR,!# "# &NxrZ[|y`QTRONPKKPNCERQQPPPQQRQPRRQOQUTQSSPRTPPMMPTTRQSRQQOLKLMLGCEE?7& ###''&%$%'('&/)&?l`606<<52:;7<7)+5'"-$(1334/-%!4CDEFKRXYQB2%")"?U]SCRo~nOV[WXXVWXTQVYUTRKAD>;CLKINKPQCMrqNSc`Y]`b`\YZZXVXXYYYXZ\]\X[[`ZHY".1/21--+),496/+<>;79=;6;7>@9=bbJUVQSTRSUUXYWVXWVXXXXWXXX[]\^[Rl~cJIOLOQPPQOLNLIJNRPMRRRSW[XRSLDABEFEEDFHHHKNOOPPPOOOQNOSRNPVQOLHFDBAIIGHLTY[^_]^badnf? :[YURC1?ADE@@EDABD@@CC?@DCCCDDESbhcggfhgeijichhI($"#"2aqWX}eTVTRSSOLPNEDMQQQOPQQRQQSUUUTUTPORQOOPURQSTSPOTRPPOMKKLKFCEFEKNUSQQURA0*!"'9NRBNnnMS[XYZWXYVTWWUUVMBB?=CLLJMGORDMopTUe`X\^^Y\^`^[^^Y_[XY\^^\\^^a[HY$16574-++')395-*EJB8865:<:99=5EbHUVRTUSUWUVXXWWXXXXXXXXXXYZZ^XTv|r^KFKMQSQQSPLOMKILOOKOOQTUVWZUTTTSPJFGEDEC@AELKIHHIJKOMMNLHJNMMLKJIIHMMPQQNMMQSRSUPOW`D1P\WI60?DDKF4*.5@AA??AD@ACCBCCAECBBBCCCBCECDRcjdgggkhcbjihkcB&"!#!"Cq~~qVUu}hWYUTVRRQOMGFKRQPPPOPRQSTTTSRPSQORUSRUTSSTSQRSTRPPPNMMHHEBEE8'"" !%'('&')*)(,&8^x~Q0564:32>=597 ""(0/%+2.2>6#&>GC>RoqNRYWYYVVXVUUSQSTK???>CMOKKHKLDQqpW^f]X]^^\WW]a]Z]aa^\]`_[WZ\[_ZI\.=DEF@62+&(394,)BSG4;>68=:8;D87hŪcKTUQRTRTVWY[[XUVZYYYYYYYYVUW[TT{vdWROPMRTRRSQLMONJGHJJEJMMOVZZYYYYYXWVWSQPLFEICDEGGHGGHHILMLHFGIKIGGKOLJIJJKQZVWTUXX[ffP*-G[V?2:<@GH>1+,4:AHJJMNMLNOOLIIJKIFIKHDFEFGDBFFGEDFEDEEFEDDFFDFFFCCEFEFHFEHIEDJGEEFEEEJFCEFFFGJJHFDBA@@AABAABB@@AA?@BB?CDCC?AHBCECDRckllgfjhddijmo`>$ # ,QyyxrWTkxgWZVSVQQQSNEFPSPNPPNOSQTUSQRSSRQPPRSRRLORTQOPSQPPPMKKNDEB@DD4!"!%(&%$%'('&,&?iz{H*486:44A?698!&.$./#-701?8%,?AFMKB5%"!$,;Zr{sRWYXZ[WWYXXYVSSTLA=??DMPLJLOK?QwqMVa_^^WW\Z[]ZVY^^_\[]``]Z^_]_YHZHpA3'.-/4'(CRK<6:A:4::1;?8LĭbFPURQQPRRRSUVVVWXZXVWYYWUTZ[`SUt\NMMRRTVUPLKKMLKKKJGEFEGLOPSV\\[[[Z[[ZZYVSPPQPMKJJJHEGIHFGKNMLKLLNQSUWWXY\_bdfhgdejmlpY,>aV80?@BJL<+**2?EJKLMQNN>MypEPc_Y]YX\[YX]c^Z\\\^^ZWX]`]\`XH\gĻR.%0..21FYSFA>;3;==5;=6B{ɻaIQSQRTRTURTUVVWWXYXWWXXWUWYX^SXnYPTPMRSQNIGGIJMPOJHILFEGKMNORXXYZ[[[[YZ[[ZZ[\XWUUTRNKIKJHHJKIEGIMOQRRWWWXY[\\`cdbceebaS.E`K/3>ADJI9**+0:CHJLOONOQQNMNIHIJIGGJIIHFEFHGEEEFFFEEGFEEFFB=DBACEEB@DCDFECEIGFEFIJHDGGFGIJIEFFFHHDAA@??ACCA=@AAA@ABCDAEF@CGBAACCGWfkhgghecdhihir^4"!##"$#HtsxqYRZ[[[YVSQSQQRNDFPURQRRQPPTSVWQORRRNOSSQRSSTTSQPQQTQOONLJJFGFC?7+! ###$$%%%&0UwzwzB-437:62:<;<0$)<3-,"06/0<9("3DA," '-..16:<@PbZ<&#5]jMU[VX[WVWWUUUQUUJED?>ELOMMOOPAMwlBH^^X]]\\[XXYWZ\Y_\[\\[]``^^c[K^c_+ 'CU<=WSHG@>?<9;9;>9;\ĺ_JRSPRURTUTUVVUTTUWXXWVUVVYWV\R[oZOTSOQRSQMIGGCFHHGFGIKHFFGILPSUWYZZYYXY[[ZZ[\Z[\]^\YVRTTQPPNKNNNNPRTU][XVWY]`^aa][YUPTO2>R;(4=AGJE4)*+.8CJKLPPMLORPNNNIKMGFHFEIIFFGGGFHHEDEDABCDDDEEDCEEEEECAACFFEEFFGEEHJJGEGGFGIKJGGFFGFCBBAA?>@CC@CCCBAABBBFEACDCCA@CEIXglffhihddgjfgqZ/ "#$!"(Ry}r{v^UWWWVVUUTRQQSOGFNVQNNPQRRSQSSOOTUSQRRPOPQQRSSSRSTUQNMLJIIGECA;0&  ####$$%%&:c|wvwA,5459529<;1" &('&%$&+/1159BQXG0! +5hxjMW[VXZWUWWSTVSXVICC=;BKNLJHKPAOvmFB\_\a`[W_]][YZ_^_\Z\^\YX[YZ`ZJ\et3@17URJJ?;E?8;>8GZDSTPQSRRQPRSTSTTUUWYWUSUWVTTZO`si\PSSRPQSSQNKJJJJIHFFEJIHIHHHHPRTVWWVVWWVVTUVYYZ[]^_^]Z\\ZXXURSRQRTX\__]YXX\`cZ\[WTSPKa_A,:2)1/(*+-6ELLLQPIGLQROLHGIIEHKFFHGEGECEDEDBCFEAEEECBCEGHFDDHJGABEFEDFGFGFFIIGGHGGFGHIIHHFFGDDCB@BCCBCBBCCCA?>??BABCABCBA@DHLXflefhkiedglfhqV+ %!##".[~v|nWPVUTSRSTUSPPRRJGLTPMNOQRSRSSSPMLNOPTSONQSSQPQTVTRRPONKHHJIEA?7+"  #####$#$(EmzotwA,554741:<<;/);>%,(#45-3;2$,5.%-5692/064-,2*/335@MV9'#=|~jNYZVWZWUWVRTVUXWHBE?>EMPNMFJOBOvnLPa^X[\\Z]\Z]_[Y^`]Z[]_^][Z\c\L__hl~)9PNMSE:?F>@C>?@6Ap\HRUQORSTPQRTSSRSSTVXVTRTVQQUXMgd\XTUTPPONNOPQQKLNMKJLNJKLKJHGEJLORTVVVWVURRSWZZZYXXYZ[Z]^\[\ZVSTUWZ\^_[[\\\[ZZTWVTUZ\ZfgL!  ./-1;FF;6;@??C>635417CNB8$H|hMYZUWYWUVVUUUQVUJEA=>CIKKLLORBOtmOYb^[\Z\^ZXW[a\X[[ZYXY[\[\Z\b\L_Z}o@NUINYODB?;;@==D<:c]LOUROQUVRPQSTTUVWUUVUTSSTNPUVKpuZUWPQRROOPRUWVUONMLLLLLMONMKKIGEGIMQTWYZYVROPRUTTSRRTWZY[\[[]]ZZYXXZ]_aaa_^[YWVXZYWY_cb[aO)"./.4=CJE4(%&)/;GKKLQLJJMMMNQJIGHIGEGFGFCFIHFEEDEEEEFDCCDCBCDFDAACFFDFFFGGFFFIFDEFGGHHGFFFFFGFEHHDED@B@?AA@AC@AAA@@AB?EB?DFCDDBGNQXchffhjiffjkhllH$#&! #".b~urxfXWXVTQPPPQPOQTQGFOTSSTSRQSTPPQMNSTQPQSSSRPSQPPRRQONLKMLJGFIE?9/&"" ####""!!0Zvuqw~~K.562310:=<;-#0@<""&:7,7<-!$6C@9=HTMB;72-<4Y|ZHNTROQSUSSSUTTSTTUTSSTTSRPQUSLykTUTJLRSPPRWZ[XURPNNOQPPNPRQPOMIFFGILQUW\[YVQNMMKLMMNOSVUWWUVZ[ZYWUTW\bfgd_ZXWWX[]]ZZ^^\ZaU1"+,/7>BIC1&%#'1?HJILPNJHJMMMNKIGHJHFHHHHGFFFCEDDEFFGHDDEDA@CGDDEDCBDGDFGFFFEAGFEDEGGGHGFGGFGIHFHFBDFA>?ABBA@@BBBBAABCCABBBDEBFDIQRWafgfgihfhlggkf? ""!!# '[xop^TQVUTRQPPPMRSQMHHNURPQRRTVRNPROPTTNRTQPTVQOPPQQQRSOLKKJHFFDC=3)$"! ###""!!!?hxqvyxR0462200:><;- 0G@%&;6*8;)!0@A96>FKSXOA;BJTTV\^WI>56:>:&/^eHVYTVYVTVURTVUYWHAB>=CJLLLJMNATwmQX_\]_]__^Z[\[^_\Z\\ZY]``c_]`XJ_\phQ5.*!'( !" &!$%$125U`KOTQPPOQQRSTTSRRSVTRRTUSQSSUPMjQNNHPSMQPOQTVVTSUVRMKMQRSRPOQQOKIHHILPRY[\[WSQPHJLLJJKMNOOMNRTTSSRRTWZ\`^[WUTUUTXZYZ\ZW[aW4 "(08?BIB/%%"&3BIHILPQGAEMOKGJJIGHIGEJDEHDADFHECCEEEDFDCDDDEHEDDEDBBEECBCDDEGDFGEDFGFHFFGHGIKFEHHCEE?>BDA?@BBCCCB@?@ACB?AEA>CHEJRSW_dhgghgfindfja9!"%"#!GzzmjZSOTTSSRQQPPPPQNGGNVROOQQPPNNNOPQSVSSSRQSTPPQQQQQRSONLKHEEGAB;/%""  ###"!! NtxkvyvV2463100:><:-0I?% %;5)7:'-8<>@98GPMPWSD8578IF-;wdHVYTVYVTUUUUURVVIDD>>88+0OA%(&*D2'89+%09<@;6EKPRPRVSL>33::AQ]Yc^HLYE*\~fLU\WXZVTUUTTTRVUIDB=>EKJJKJLNBVwjRY[X\`^\Y]^[Y^_]\^^_`_\[]^[ZbXHdRgD0(#  !!"""#$%'(())+:g`JOROPROOONOOORTSPSRQRTUTSOSSMViPPPJKKPLLORSTX]YTUWSPRTRRSSQPQROOJFHHFJPPSW[\[Z\WROLIIKGEFIIGFHFJLLLOSVVTQONNOPTSUWWUUXRST> (49>DD8)  *7>CLPLIJ>>FFEGGHKLJHHHGCEGGFFGICEFECCEHGFDDEEDBDCBCFFDACDEFFEDEGFFFFGFEIGEGIJIGIGEEDBAA@B?=AB@@C??B@@BCBDCAACCACKPPQX`e_`ejjeejjmnU."$ ! F[N@DOXTTROOQSRQMKOOHFLQSROORRPMOPQQSVYVTRQQQRSOPPPOMMLJLJGHJHEA7+(&!  !!! "# (#:gwsrrp{l:.974.0==:9*0M=#)*(=3.4-#,::;;:BMOQSUWTQSF>8.-5JfKUZUVZVUWVRUXVXUIDC>>FLLKKJLNCVwjRY^ZZ]]]Y[[Z\__]]_\[]^^]\c`]cWE_F|YC2$#%# !!""!#&()(&$'/ChbJOQNPQNNNRRQNNPQPSQPQSTSRQSRL\iPNQMLJMPNNOPRTXUUZ\XWWUQRTTPLOTTSNKMLHHIHLUZZZ\b]YXWSPONLJIHEEFFEFIJLNPPLIKLKOTVQPUZYTQNOVH#(49DDGPOIJH?AGFCEDHIIHFGFDDFHHFEFHFEEDDDDDBEGGFCCDCDCCEHFCFDCDEDEFEFFFFFFGHEEHIFGHEFGFFEDBCA?@CA@BB?@A@@BBABCBBCCBGNRRQW_dhddggfhmemlN+$'!!! 0?;6ARWSRRSRPPQPPLLPLGJQOOQQNOQURPPRSSRUUPOSPMPOPPQRPNLNLKKJIGD;0(%#!  !" !"&%JotqtuqyvB.77303<<<;( 6N9!)(.@4##'<=::>EMNLKILODXxjQX`]Z[\]Z\Z[]\ZZZ[\]]^_aa[[[bWIeIt~bC.$%  ! ! !!!!! !$&'(''())=cy|~{dJLRONOPPMNQSSRQPPOQRRPOQRRSOJ`fPLONMIHKJJKNPSTQWXW[[VTXXWWTQQSUURQQOKJFEHOUX[]Z[[\]^\ZWURPMKJJMHGJKIHJJGFGHIKNMORSOJKPPTaX0'498BB3$'6CLHFKLEDND@ELIFDDIJJHHIGEEEEFFEEDIEBCEGDAADGHEDDECEFEDEFEFDDFGFEECEGFEDFHEEFGFEFHHIFBADC@C@@CB>?EB@ACABDC>@BBBCBBJOSSRV_fhggfegheeqhD% "" ! #%/43CTURPQUTPNPMPLLPLEIQQPOMOPQRRQRQRRRUVSRWTNORQPQRQNLIFFIIHEB7-&# !  ! #%.YsrrwuovL,37324<:=<'";P7!*'1D4 ":A869CSQNLMPRSPSWXUTSP?;9.5V`UGMueKUXTUYVUWWRRSSWVICE>]qutu{}fJKQONPQRNNPSTRPPQOPQQONOQRRNHfeTLLMNKGEIMMKLOSWZWUZ^[XZZYWVVUSQRTSPNNMLKGEIPVW[_`ZZ^`^\[YVTRQOQLILMJGGGIJIIKKIDFJJEBGOZ_nh<&4::@=-%1ERJCFLIDHNAAHMKFFGHIIHHIHFEEEEEEDDDDDDEEEEECCCGGECEEFFECDFEEEDFGFCDDEEEFFGCFHFDEGGGGEBDGGC?@A?>?@@@?ABABDB=?ABBACDLORSRV_headgeefcfqc;#""" " +32?SVUQOQPNOSRPMQVLFMRSRONNPPKOSSQPSURSRRTTPNSQONNMKIFGHFEHE:3'#$"!"##""!  %$7erntxsmtX..7324;:<;&@?@A@=>@A?BC@>?ABABDIMORSRT^hlbelfadego[5&(&$!! $17FUUSQPQPOOPPMIOTLDHRQRTQNMPNOOQQRQQTSSSSUUQTRPNMLKJKIJHDE@4)&$$#"""!""##"!! &$?kpktxpnth8,6312<:98%!BQ2#/(CB?AB<=@@@BC@AABCCCHNQQRSQR[fgdii__d_lqU0&'#" ! ! -;LVRNQRRQQOLNMMORMGFNRTQOPPNNNONOPQRRPQQPQRQRSRQOMLLHCEFB@;/%)(" !  !"#$$$ !&$!ElpgrsnrvyG/44.0<:65%&HR. .'79##'-%'/.1@A<>A@=>AAADEADCCEDEJQSRSSPOXc]abRH[jaijL*&($$!"!"+6EOQQSRMMQQNONOTSLHIOPQQONOROOMMMOQSQPQRRPPPMPQQNLJJJHIF@A:)%(&! ! "$%#! "&%#Ilpdnpnvx}T235,.<:43%&HP*/*#7. "$% '+(1014;BIMOVURUWPO[JAXv|cKVVRTWUTVVWUTRTRE?A=>FLMMNIKL@TuiQY`]Z[]`^[\ZZ]\[_]\\_`^]]W^aaSHg%*)'#"& ''$'&%#(% ""(1BQO@77?atqqtuxkt}\INQMNQQPNOOONNOQRPPONMMLLJRFScNKQOLKPSOJHIKKKNLLNPQTW[[\]^]\ZYWTTUWXXXUTUTQQTOKIKKKNSXWXZ\[XU[XXYYWX[``acgkoqqlkopmklqa[`P% !-<85,'4JODELPNONB38ISOHDA@HJIGFFECCDEEEDEEEECCEEDECEB@BBACFFFEEDCBGFDCCDEEDCEHGEDFGFEFGHHGEDDEGFDBB??ABBBC>;=BB@?=@@BA=?DCB?=?FNUXT]X97]menj@"$#"'$ !""'7IRQORRQPRQNRMLRSIEITPMOSROONNLLNMOUSQQQQQRSQQQNLJKLFIKHB:0)$#"!!!"#"##!"%#%$(#&Mpllmoopxc(*2-//+0.!+@F' 0(#/()!" !%(,))-/4<=@IHRUSUSSZJ=YyzfNUZYXVUSRRTRNOSPFAC=:DMMMKDJNCWufOX]Y_`\X[[_^ZYYZ]]\]^][[]a^\aTIh*+#"&%$!)/(#)++-+'%&+5?EKJ@:>@?^{~}}VDMPKKMNONNOPQONMMPOONMLLKKQDRfPIMNPNNONMLLLMMLKKNPQSVSW[___\[\[ZZZZXWXZZXUTUWTQNMJIIJRUXZZWUS[bfffijjdfilnonnolkllife]TU_S+ ,<>5+)7LOBEPQLLE:4DNQKGECBFHIHGFECHFDEEFCAFFDCEECCDFDCDDBEDDCCCDEFEFFGFFDCFFHIEBCFHGGIHEDEGGGHHGFEC@>>=;:::898679844895687:88?ISVVYVN:=[f`lc:!$!$# ""(8IRQNQRPPRQOOQRQMIHKQMJMPPNPLQQONNMOSUTQQSROTRPOMLKKKGCA?8.&'!!#"!# "" !"#%$(#'Lollmonows5&0,,-,++(*00!$-&/3&")"#*..**-049=>EOTQORUG@YxybLUYXWVUTSSSSQTXTJC@<;EMLNLFJJ?VveM[^VZ][Y^]\\\\^^ZY\]]]]]]_]]bTHh+) ('#,2.&%*,+).:FNZszI0?C2:W~~~}{xusqpmmnnnprs|~}ywR@LOLLONOMNOPONMMMOONMMLKKMOBSeSMLKNOPMNPOMKLMQNKJKMQTYZZZ[\_b____^[VSSZ^[XXYXWWVRQONLKQW[[YYYU]fkorrnlkkklmoqsrmhd`YSQNQXO. *:;/#(>NKBHROJI<17KOLGFECBDHJIGFECGHIGECDEDEDDGGEFEFFDFDCCEEFFGEEDDDDDEEEEFGHHFDGJIDBDC=<>@ABA@>>>;878988936:97:=;=;:967::646?KQPLLHD9@\gejY2 %"! !")9IRQMPQPQSSPPOOSTMHJSQOQSQORQPMJNPPOURRTUQPPQOLKLKJHGCA?:1($%%#  !#! !"!#$$(%(MnllmonowG$+*)+,''.(*')+!,?+)--.-1.2:=68:6587?:DQTTVRBB\v^JUWWWVVUUTTTRSVQF@@>=GLJKJILL@WwhPY`Z]\YW\]\][YZ^^Y\^^]]]\[[^cSFf%&-CU[cmnhdWKXknrsI63Bj|xzxrrxpooonmmllkkkjjiimjghlopoz}xwTBJOLMONNLLMNNMMMNNNMMKKJJLNBUeSNMJMOQNPPOLJIIONMMMMNORSTWY\^```__]YTPMTY\[\ZYX[[YWXVRPSWYYXWW[UT[djmpqnjhhjnqfe`XTSOHFEBC>+ (76*.KODDMNJIF5/<667878::;:;@EHD@8?D8=[kkhO*!%! !!+8HQQLOPPQTTQPQQPPONNQQQSTPNPQPOONMMQOQRSSTRPMLJJJJIIFFE?7/)&&$""  """!"###$#)%)LmllmomnuY'")**,&%/)+&"*')A,&)'>DFJBAKI?:<=>>;;38FMS]dJ>Wxy_JUVVVVUVUVXWSRTOFA=;/3BIHFDDA=>HJJHGGHHFEDCEEEECDCCEDBBEEFFEDCBFFEDEDEECBB@@?>>=;978998952269;;;<<;989:9655755663588897<@@>>?@@AAB@?<;:5@I<:TeieF&#&   !,9GPPLOPPQTTQUPJKRURPNNORSPNQNNQRPMNPLORRRRRPNNNLJHIJGE>4-,)$%  "! !"#"$%###)'*Lkmlmnmmu~i-)-)*(%+-&!,#,6)"$+?BCH@BNWPC843;GBA=:9;PjYCVxaLVVVVVVUVUUURRTPHDA=;FNKIFEKOCXvgP^bY]``[ZZ[YWYXXZ]^^\[[\\YZ^cREf3>PsErM62;CFVj}xvsrpnjhihgggda_d_^bdccfeggffiihfhfehgfhlllmmmnndhkjhhmqyx}wWEIMKJKJLLMNOOMLJJMLLKJJIIILE\fQKMMMLMPQQRQPPPLNPPPQPNNNORSUVV`_```]YVSLHMQTUXUWYZ[\\ZWTPPRUVVVWURSZ`bccb_[UPLOMGCBCCA=A@AD9!-,,IKFFDH@CD4-;IKIEDD@>CJKIGGHHGHIHFCCDFCDBBEEDFDCDEBCCA>???=<:9:976679:9;;::<==9<=<;<>?;;:876777447899;<9=CDCA>HOMC@CDCBCC@<99;;@H@>Qbj_?"#$  !,7ENNMPQPQSSPPNNRTQPSRQQRSQPTNOOOMPRQONOSSPNPOPOLHEEFDE>0)*)$""! !"! !""#%##"*(,Ljmlmnlls}z;)-)'(())#!*%($$!3@@CFAEQPWWQI916CKMMI>>BEEEEDD@>A7 ()*@@@A>;<;99:8;:999:;;:;<=>===9:;9:;:868;:7347;;::<=>?A?>?AAABDABBAABAAOPDADDABBB@><;;:@H??VgnX8 !   !,7CMMNQRPPRQOORPMQUTPURPQRPOSNQTQMMPNQRQPQSPKNMLIGFEE??8,%%%"$!!$"!! !"#$#"!)),Lhmlmnlks|R'(*(#%*,%" $(#%:@?CGDHNSONRTL@92:HX`YJ?@Rirz]JVYXWVUTSSTVVVVPGCB>;DKJJGHMNCZyiPU`\\[\]b^XW[]^^Z]^^[[\]]_\[_QHk)&>ļlV²mC70'.31,)))''.02E^htmijhb_`[Z]`^Z[``^^`bcbaec``acdcbddb_`ceab`^aefbabcdddca^ca`gfdighhijkllgiklkigfr~t{z]LHMKJJILLLMNMLLLMLKKJIIHHMJA\fTKFFMOLJQWXSOOPQTTQQRQNINQOJJOUVZ_ehe_[Z[XQNLJFJGFHHEEHFHHGFEGIKJKOPPRVWUPLHGGGHFIPTPMNMH<8<8'%5JQMI>:FEEF;0<989:<;768::<:89:8:=<;;<<<<;;::::9876899:=A>9A?@C@=>DJHEDEEEDECACCCBC@BDC@BECAQQA=BDACBAAA?<96EN=0'&)'"#" !""!"#"!!#$##"!**-Lhmlmnlkr{f1&&'!,/')&"#+!*A@=BEDJNQNRWSPRP913G`yxZHVZYXWUTRRPQPPRNGEA>;DJHJJFLMAUugQZ`WY\_[ZZ[[]^[Y\]YZ_^Z\ab]Y]QJl.12e]P[;9>822001-'&'%%'()/6LYca[[]^W\ab_^_b_abb_]]^ba`__acddcba_^]\^`ba__accdec`^^_bbbbbcccaceecdhlghc_aca`l}swz_FFMLKKJMNLKJLNNMKJJJIIHHHKLD`gVMKLNJJLTUPNSUQPSSTUQQUPPQQNKNRUZ`cfgeaYXXVQKJMKGEFEA?@=AB@>?@?BDGHIKOQUXXUUZ]]b[TYdaZ\_J=;;;/ (Hb^ME>AILA>95DQJE<:?BCDDB?==>;9;9;949=9;;9<@=;;;=>;:<;898:97:;98;9;AGHGFEEGGJMJLHHHGDAACGIIGFGEAEDBBBCCCCBCEFDDEDNNC@CB>C@@B?:8;=EE>>SlmP+"!! ,4?MHMPQPNNQUOQRPMLNQSRSTQRTSQRPPQOMPPQQPPOOONMJFEGFCF=+!')$ " $#"!!$$ #&'#&+%1Riiioqmlvu?",-!(3)"+%.1%&:B=?FFAFQPRSRQPQSUP>49`jbRGFMXZQ52100.*&!"#%%$'*1CV]\]\Z\^^]\^]\\]_bdd`\\]_abba`^^^^___`b`^^__]Z_`bb``ac]]\\\\[[UWYYXZ^abca`chhgow{dJHOMLKIKKQOLLLMMLJJJIIHHHGHB`hWPMLMKMLNOQRSUVRTTVUQOSVQMKJJLOPSW\bhhea^[ZVRRSROMLKGECEBACB>=>A@CGGFIOOW^beiiggc\^ffbd\K@;9<0 'IdZEA@DKF@=:>OVJGABFC=989<<;;><9<=?==;8?<:;>=<;;;989768;@CDDIKKKOOQV[][WLHMPPNLRJIJID?>AKNOKGFGIDFGFCAABCABGIGDCFNMFCDEG@ACC=78=>FE?>TllK%!" #!-0:KMLORQOOQTRQOOOONMQOOUXURSRSRSTQOPPQQONOPOMMJGFHGDE:*$((#! """ "##""$$"&)#2Tjkikoopv||B'4;.,+1%$-%67'#(B?===<;<<;:;9;94787679754428?<??<98>FF??VniF#!#%#" #./6HOKNRSQPQRRRRPNNPQQTSQSQQUUUTTSPNOLNOMMPOMLLJGGIHEB6)&*&!!!%$"" !"#""#%!&'!4Wmkijmopu|S+0?B?/)!$.$>;% #9F<<>BCCIQRQPPRSRPUOSWOID87MdqgJHf]LWYTVXUSTTVURQRNFCC>?GIEFLHKI?Zt`OZ]XVXX\]XZ\Z\][VY\]]`]Z\ZX\bPEg$-38COXQPTK7=NH>IVYTQUYF2/61/0)%('$#%%#$%&.BY`\WXZ\\\YWXZ\\ZXXYYXWWWYZ\WWVUUTTSPRUVTUWZ[]^_]]_afgghijkkmnnnoprtsrqoorrmtzgFGLIIIJMMJJKMMMLLIIIIHHGGJGAdlWPOLLLMMONMPUTPPRTXYVRSNMMOOMLLNNNRX`dghd_ZXXZ\\^]ZYXUPQRQPOPMIJFDFEBDIFJMQV]bfdjhbbhifZSLBA>;;=<>CF?DGCDJB:KF9222141/;C;FG@?Yoe=##""%##"026EMJMQSRQQQSRPQQRQQQSQQSTRRSRRRROMNLNOPRRQNKLJHHJHE>1')*$!"" #"""####$&! &%!8\nfkmomlr~c0(:FE0($#.& C; $$2CA<=?ABCIOPQRRPPQRWNPSQSRG<7BWgdUMa]LXXTUXTSTTVUSTWSHAB;;EKJIKJLI@Zt_N[`\ZYZ^_\]\ZZ]^^[\[\__\[][^aPIl~!).4?O[ONXL-+:9CRTOLJC?>=>@?7:>=?JSRKW`VQSK>KE4(+,,243=A69EDMTWUTSTWRPPUZ\[ZNHOQIFGIHIMOKDCFNLKLKHGIHIIHGFFGKKJGFGJLRWO@=BC??@@>=;:9>EG??[p`3" $"#263@JILPRRQQPNOQRSRQPSOQUTQPPSPOONNPQNMNQRPNMJKJHHIHD;-'*)!"#"#! !"###$%%!!&$%@amekmmnkq|h5-;C=.1.%%1+$F?#*6?C==>@BBBGMOQRQPOPRNOQQNPSSJ6?EFcp`N`XGSWRTWTRTTRSSUYSHA>9;FNLIIIKH?Yp\L[`][[Y\\UX[[ZZ[\XY]\YZ]^[[_cRLotpu .12fsTJMLMLINIJPRPPTUVW[^\XYYVTUUQNMJKLNMOU[\bggc_\ZVWWX[^_][[\]YSQRQRRQPNIE@ADIKMPT\bhgekkdbZTMHMH3:iiG8;>997=JTPJIHA<>?CD@05=ACNVUPUYRKLC9>?1(/1/7:6<=6=IIOSTTUUSSURRX]^]\TMOPNMKIFLRQKEFKIJKLJHGILIGFHIIIFIJKKMNNNRMEAB@>A?<<>@;6>]p[/ #$&% #460I@,19.(41(JG&5HE?@?>@CCBGNQPOOQRQOMPPPSRRWE=[fJAY{hQ_YGTVRSWTSTTORSSTNFC@<=DIHHKILJBYp\NY^ZYYWXWXZ\\ZZ\_]WVY[^_[[[]^MImzx$00/8IVPKWG-6JUROKOUQLQW@028622,)'()'&%&'(&%%,3P[`\[_`]a``beggfefijkkjiklllmnnnsrqswyxwz|p{lIFJGGJJKIGILLLKKLHHHGGFFFGAAmtTLOLLMKNJKQQLNTQQSW]][]ZWTSRONOKKMONLMPW]cdeeca^[YYZZZ[][]^\VSSVRPPQOMMNJIIJKQXVY`bcknhfZSPMPJ74epP77?:;7?NRJCDF?;78963)63)LL) 9KFAD@=?EEBGQQPOOQQPOSSRRROPXB=a~eNSypOb[KWVQSVTRUTPRRRRLECC;:BKLHEHLLD[p_T\_ZXZZ\ZY[][YY[\[[]]ZWZ^]\\[IEi .348BI\OQ<*>PTGNLGKMKLWF2.7:415.)))(),*'%&''')?]qkchmjmmmkiknrooopruxzxyz}~{uoqpghg[bo}wu{eDFJGGJJKIJKKIGGIJHHGGFFFFFAFuuVORLLNNNOPPMKLNMNOV^`adba^ZTPRVSPMMKIJNTWYZ^dedc^ZZYWXZWYYYYYUPZTQVXTSVXSOMKKPWWV]bfoupgXQPOQJ80asV54A=@?ENNID?@<;=@ED?6:A@BMLHHA:?F9.441;>5499=EHLTXVSQQTSTUW[]ZURMLLNMKOPTUPLJHEIMOMLLMLLKIIIJJJINPMHC>:@LMA;?BA??=;=?<6;CF==^pU&!&& -61;KJIIJMOPQRRRSTSOMNOOQSQPTPMORQPRQPNLMOOMKJKJGFFC>4)&& "%# "%$! "&'$"$#"$&5Yndcmkjokm~v<*;G;'2>3)51(MJ,#7DFEE?;>EFCHSOPQQONPQSMKPRONRD9\zvSUomXnYJWUQSVTSTTSTRSTNE@?;=>DOJ=?DB>BA:8;;77;GH?=]nH & ($ &+ )11>IHIJKLLLKLORRSTTTRKKRTOOTSNPSOLNPPNMMOOMJLJJHEEA9/+'$#""! !#&! "'$!##(#&?\ijiffijlry~~s;%9I;(,<-(5,,SJ" >CDAELOOPQPONNOPPPOOQSF=YvecpoezZLYURTURSTPQRPOROF?@;?GHEHKFLG?]u\M[`^[YX][Z\\ZYXXWY\[[^\XZ]Z[aSHa~}|||~| (028D@CUS6/JWNMQTUY\YTdbA*3<:6=91)')+,-+)''()*.0Cl}xwxkifc^[XVTTSRQPPOQRRSTUVVUUWURUVQQkwuw_FHJGINLJPJJJIJJHFGGFFFFFFI?Dq_LIGLNQPPONMNNNMOPORX]_aacec_\[VWVQMLMNSPPSVXZ\___^Z[]\ZXUTVXYXY\`dgijjeilljhfdgeccinlfe]XRJHE<""Otb:2?A;FPPH@AJFKNGERSG=COPHLURLORHEMKCGO?.44/5<=<89>DGTUTTUSPQRMMU[ZVTMOOIHLNT[^XOKE??EIKKKLKHNLJJLPOLEB@A@>==CHEA@??BCA<:=<88>HF==]i>#$%%+6'#(/.9EJKLLLLKKGJLOQSRPOLMQPLMSUQRTPMNOONMMMLJIMIGGGHA6,'$&$  $ "%# ! #%#"$'$*E`hgggijiir|~}v=(>J;)/=@EDBHRSOLLNOOOOPPPOOQSHA^xxokrWJWWTTROQUTOOQUUKBAC9:FMMHDELG@^u\MZ\XWXZ]ZZXX\]\YY[[YZ]]\[]]^_OHd|z}}~~||}~{z|}#,07B<=WZ43[dZZZXVX[[XXhU/,88<;83.)%&(+++)'(+.000M}{zupkhifa\TTUSPNNOOONLKIHHPPQQRRSSSSTTUUVVVUVURTVRH`urqs]DEKHDIKHIIJIGGIIGGGFEECCBI>BtbOLLNNOOONMMMNNMOQNMPW\`__abaac]YUTSPNLRMKMQUY]VY][XY\ZZ[YXZ\ZVVXYYY\aeifddefggllkiknmilf\NCA?8%!JsmF38:9ELHDBELGOVJGOKG@AJKEIOHHKPHDGB9@I;+01-4>>;78>DFLNPRUTQRUOOV[YWXIIMKHIKO[\UNMICCIHFDCCCD@BEHLKE>>=>@B@?>BKLB=@A>A@<;><76ED@=<>CEA=EPOQQOLLNQOPPOOOQSB>_z}|rozSIWSSWVQPSSORRQQLDAA9=GLJHFFOMB[nYO]]YZ^]\VTUVVXY[[\YXYZ\]\]_a^KHj|z{{|~|%*4@:4LR.9hjcbdfjptts~w>(6889765/'%'#(,+)(+/,2.1Uzvtromfd`\WTRQNNLKHGFFJKKKKKKLLLLMMMMNNNNNMMMMQRRSSUUUVTVVRTURH[qrqs^FBJMHEJJEIJKKJHEBFDB?;8548+1kmSKKKKNQQPPOPPQOQQOKMQVYZ]abaadea^]YTRSQPPQQRV[VX[[XY\[[\\[[\\Z^_^[WX]bmifgjliegjkiilmkqiXF<>>9)DqtQ:99@HMHCEHECKMCAD>>>?ED@IMCEKQJFJD9=F;-22-0;;735=DFLSTQQTUUWRPV[\ZYLGNPJHIM\\TNQOJHNJECA>>?A@?@ABB@>?@BBB?>BKNE>::=;438FF==^g8!"#"$<@)!*+8FLLMNOONNMNNMLOQRRPOPQRQPMQSQMNPRPOONNLKJGGIJFE>3($"$#  ! "%%#" &&'8Wigbfgjkhiq}z{p9+@D4(02,++7H6!*?FGDA9=BD@>EOSQONQSQMOPPPOOQS@=]y{|~QJXPRYYRPRRRTQORPG@C;]u^R\[WZ\[YSVZ[ZXZ[Z[[[ZZ[]^]]a_JHq$/?;8EF6[U)085:875.&$&%'))(()+0/205Nc_[WSRRQOLLKJHIHIJIIJJKKLMMLLLLLKJONNMMLLKKKLMOPQQTTTTTTTTWSUVTSUSHVmrqq]EAEIIGHIIKC=??;87321/.,,+3&)ftUKMKKMONNMNNOORSSQOMNPVW[__^`ddddd^VSTOPSUVTTVYVWYXWZYXXZ\[YZ]XZ[XUTX\afihikhbehiedeedi_N@?BA>-:iu[EA=AA=7FINUURRTTTVQORY]ZVNIMMIIKNWYTSYYRPPJEDC@@B??@BCB@>??@?@?><>GLB;=@===:9=:42>0)03,*&8M9"*>EEDC;BGNQQOQSRNOPPOOOQSF?\v{PLZSSWVOOSUSRQUXPEAA;>FIGHJFIE?`v^Q\[UVYY\[]ZZ\^\[[\_^\^\\_\Y]`JIw!,<:7:5?~e0,97:9972*%#-)&%')('73591-9IPNMMNNMLNMKKJJKKMIGIMPMJIHGIKLLJMMMMMNNNLLMOPQRSVVUUUUTTWSTWTSTSHRiqonZCCIFCHIGII8+-2312/00001115+.h~uULQNLNMMLLLMNOTSRSRPNOUTVXYZ^bdfiie^YWSQPSTUUWXTTWUUVWUTVYXVW[VXZYWUUV]gkd_beeadda_``^]UHBED><12`vcK@:BKSL@EKFAC99<>@BA>=?@???@?=?JKA:=?=?>:9<;54DHD=AalA  #'#!?I/ %'4DHJLNPRSSPPNMMNNMMOPOOPQPPUVRNMNPOMLLNNLIJFDDBA8+%'### !""##!$%  "'%,Daicbifeijmry}z~zb60>:3/3/,+)8K9%.AD@:677787888\y}xyOMYRRWXRQSRQTTUUNFC>:?IJGGIELKF`rYN^]YZZX\]\[YYXYYZWZXW[YUX]V[`JJz"#*97<@4E|vmfd`]ZJ.*74466430+&+)'&')*+66541*/?OONMKKLMMLKJJKLLMIFHLMKGNLJJLMKIGGIJKLNNMMMMMNNNLMNOQSTUXRTWTRTTLRjutr_HFLIDHJHHG8-.4432100.-,++..3i||{TGNKLOQQQPQRSSSQQSSQQSRQRW[\\\]bgfb_]\]XSRRQRTVUXYWWYXXUSSTVWXVXYYWTQOV_eb]]`b^``^_aa_UOFEG@9<4 *XxiJ80@IJDCHIF@??B@:=FECJOKNURIPUKFLD8;A7,.0*)4;=65=FIPRRSUTRTTQPSZ]ZUNFFMQLHPW[Z[_\PKHECBB@=;??@CC@>??@@?@AA>EKIB>;;@@>97;955CHE>>\oO$ !"$&% "AL4 "$#$1BIJLNPRRRTTROPQQPPONNOQPNRQNNSTQOONMMMMKILGCA?=5("#!!!!#"$%# $(#'%.Hbfbehfhjjjr{|{z~O*(0*())+...))8D4'2>;60)+,-/13446:>AKTRJOPPOOOQS<;^zytwOMVPQWZVTRMPUTQQNGBB9;FMLHEHIDAcx]O\\Y\[WXX[^^ZWWXWY[ZZ\[Y[^W\_IL{ &%*74400/..01368;=>>CGstsySIQNMPMMLLMNOORPPRRQTXXSQV]a`_c`]]bgd][ZXWTQQTPTYYUVXVWVUUY[WQTTUUUSOLQUZ_`acd``_^_ba^NIBDG>:D6! OxoK4-7GIDHHBA7=IE=85;>ERSILQMEHLD@FB9?D:--.,-6>@84;CEIQUSSSPOSPPTY[XVNJFEKMHLZ\ZX\XNJKGB@AAA?=?BC@<=A>@@@@A?<>LI;:>>=?=758744;EH@9Vr]2 #%'(!'EM6&"%"$1BJKLNPQQQSTUSQPPOQRSQPPPPPRPOQPNOPPOOMLKJKHEA;83)   #%# !!#!&%/Iadafegjkhhq}}~y{zO..4,/1.-1../1;B3)14,--)+-//023374..=PVQOPPPOOQS@?`y~vNKSTRTVTTSOQPORUND?B;=FIHHILJB>aw_R]\X\]Z\\\YXYZ[[\Z[^^\\^__Z^^HM{%&'--,458>21IPHJKHGJJFBAEA<:2!!"&!'0+%*2=75784/-9INLMLKNMMMKJIIJJJKKHKNMKMLKLKKNKLKJMNMPMNONMNOPSQRUVSRUSSSSTVUQHN`rvq`KAEGFDEHIFD>25FLMPQSUVWWW[WWyj|ZHNLLNTLIOSPOQSQPPQUVQSRQTY_a``ca_accdb][][VSUPSQOUZYVXUVZZWVXZVRRSSPLOW[\aa__[]`_`daWHIHDFBFDJRVTUUPMQOQUXWUUMEEEEHGHUYWUVWPGFGD@>=<>=?=BD=648:71=EE?CTgqF!#'(,)EN6'$$'(%)5>IGMQMPVTQSWVQQSQMORQLKMOSQTUPNPPORQMKMLIIHFB<5-& "!#'$" &%%;)+1,..*./111.,*2)&'):MRTNMPJIPMA=\x|vMLVOUUPOSQLRUVWTJA?@;>FIHHHINE?`rZO`_WWZXZ]Y[YWXXY]\[]YY\ZZ\[][ILw1<-$/55892-9JNIJFFFHIIFCBBAA;+!#)0-*.0./59731.9GNJHKLHHIJJJJILJJKKIJNKLJJKKKLMLILPNLNNNNNNNOPQSUUTSTUSUVVVUQNIK^qup]E>CFFEGIJFDA;E[ba]\[Z[]_a[WWyh}_KPQOORMLONIIOVXZVOQWXQPQSWZ]__cdcffdcb`\XUUUUTTSTWUTVTUVUUXZZ[XVVWUPLU\_`aa_`_]^_]ZTMHGFEHD;AA*ArzW8003:FMG=:@JJA45BE?9:;>GKIELPLCBB>EOI525..47833>GDIPROPQOMURQTXYXXLGECEHHN][XYZSHDBCCBA=<>?A@AED?;<<=9669724;?GEJJOQNOTRSLNUTQPQMMNNKLOPMPSRPRRPNPPMLMLIJHFC<1'"! "&# !'$ %=[u|}vLN]LRSPQUUPQSSTRJA?A6' ",0+(,)'*-.--,.CRKEIHLJIIJJJIEKIFIKKLNLKKLMMKMMLMPNLLPONNNOOOOSUSRSTTTVWUTSRPIFUion_HCEFCCEIJGD@;H[_ZYYZZZZYY[VWzgnlNIILQOMNQOKMTSUYXSSUTPNORTTW\`ddcghgfbb`\YWVUUYXTUUTTRPTYYTV[[XUUUTPMQX\^```aa^_`\VOIDBDGMG>BI25j|^;.33:GMB9:FFA;44:6:9=EHFGEEJQRRRNFENH3/1,,15712?HDMSSOQSRQSOOTZZWTMKGCEFFP`]YWTI?>@AA@A?>@BB@ACB>?>=ALJ?>?=;<5378314>GF;>Tioc;#" $'"*ENC2%/-(("&;KLPROORQMPUTOMPSSQQPMLMLPPPPRQPQNNNNMLJIHHHF:*! !$!$'#!%>Zc_`cdfghjmrvx}|{~O)#%%&'"%#""'%"("$)')363/.,*)'&&!(&&+$';NSMKPRSN?A_v}oJMYVXVQORQNQQPQQIA?>:=FIHGFEKDCfv[M[\Y[]WVUZYZZXY\\XXZY[]\\WW\\KMvB?AC8.18BGDGHGMNKNQONROFBJF3,1-,05722@HDMSSOPSSRTPPW]^XUJIEBHJHSac\SNG@@CEB?????ECA@BB@?>?@?>?>=COMA>A><::96310/;FH>>UgihH"! (*#.FGB9&/0/-%);IKOQPPQQKMPQNNPRPOQQNMMLLMMPSQNPONMNMKHHGHKI:& !%(!!'A]d]^ddeghjmrv}z~}uF" !"#$"$$""&&&'$"%&&.62*#$%&'&%$$)$%-'"/FTOIPRRP@B`xrQOPPSTRSVVSQPNOPJB@>:?HKIHFAIEDdqWLZ[X\]ZZYYZXWYYXZZZ[ZYZZ\XX]^KLs*56.+9B?)(>KGFIIHHIIGHJEDIKGD>3&  (,((('((&$% )>KIIKIJKKKJJJKIJLKKLLJHKLJKOMJNOKKNPMNMMMOPPPOPPPPQRQOSWVTSRPKDSimfYF>BFFGHGECBA=GY[V][YXWWXYYRVzjpxWJNONOMMNMLLOSSXZXX[[UNLOQOORUZ^_egffjghjib`b`]WUWWUWUVWUSSVYVVWXYWSPOPU[`a^ZZ[\[UQKC?@HKJC=AJ;'VxjH7.3BRN82>?:GJ5'1;JPGBIMNEEOWQJIF==FE2*-*)15834@HDLRTQSTSQUQPU\]ZWNKGCJJFM`bZPOIAADDA@A@?AAA@>?BA>>?@>>??=CPN@<>=<88742100;EI>>SgfnV+#(#)==<5&(./("+;EIMPQQQRSQOPSRPNMNQRPPPPRSOKORRSQNMNMJHHHHIH;(""%& )E_c]^dceghjnsw||~n?"%%%&')(&#''$))%&&))$ !#$%%%$%!!('$&>SQJMOPQ??]v~~rOPVQUWWVUQMQPNOQKCA?;>GIIHGHNFCbpYR\[Z]]XXUY[YXYXWXY[][YWY\ZZ_^KJq'.:9-2B>0-=JHEJEDFJJIKPMLOPOPPO:." ".&((&&&%#(%#2GKGKFHHHGGGHHGIIGKLGIJKLJKMNMKKLKLNNLLLMOPQRPPQRPPRTQTUTRRQOJCNdkcUH?CEGGHFCDEEAIZ]ZXXWVVUUUWPWyqky|aHINNLNOOMLORSSX[ZY[[XRNPQPOOQVWY^cdeefhijjfbhe^Y[[XWVTUYXTU[VUUUVVTSPNQY^_[V^YYYPF@=ACJJE@$$NvpM6.6GUK3.=>:JF(%59NTF;CMJAEKNICFF@7@C4*.,)24766?FEJRTSTUSQROOV[\XULIEDJLHM]^UQSL@@HB>AB>>DBBA?@DEB>?@??@?>DPNA<==>;4144004?EG=;Qfgn^4" #"4BB91%"'&$+2BHLNQPOROPPPQQPRNNPPMMNNMNMMNNKJQNLMMJIHLGDC;- )'##,I`b]_ccdfhjnswy}f6&-+,+,1./.30))% ##&&##"$#"!!#%&%$##$'((;RSLLLNQ??]w~~qGL`TUTSSSSSQQPRRKCA@:;BEFHHILB@aoWO[XVZYWZX[YZZYYZXVZ\^ZWY[[[^^JKr *0?C0/A<23AKIGJOMJGHLU[][UNKJJLKKJD;.!'(*($"#" $%!*BMLMGGGGHIJLJGHKLJJJKLKJLKLMNJKNLJLMMNNMMMNOPPQQPPRUQRRPQRROMHPgqdOCDEEDDFECEEE@HY\YVWXYYXVUTOWwxmp~mQORNLMNNOQSUVRU\_`_[ZVSRQQPORTSRX]adbbcfjkhdilg``b^YZZXUTWYZWWXYZYVURNQX]_^ZVQQSMEBB>AGDABABFC+BptQ4+2@KB-.@;>L?',AC@>@@??@@>DNMB?><=8436427@@CF?;OdfjfB$!$$ 1FE>>0&+0+',AHKMQNKOSPNRUONUQPRRPPPOMJJMMKLLQNKKLLKKLHD?7+&/-!"0Na`^`abdfhjntxyX/)300..519<;/+(#&&**')$#" !"'&&(((+/=PSOMLOP?B`v~yKL\VTRQQRTUOQRSRJA@A;>EHHIIHKDDdqXP^ZVXVUYWZWXZYZ[XXZY[ZW[\[Z]\JLv!)1CI82;;16GMJIJLMJB;::9582+-.**.258@GB7*%*,*('$"$!&:HHEIIHIIIHGJLJILKHGLMIHKJHIKKNOJIKKMNNMLLMNPOOOPPQRLNPQSUSOJCH^maJ@ACDDFGFDCCB=FX[XWWVUUVWXSNWw}chvQJPPPMJKNRPLSNOV[^`_ZZWTQPQPPSRRW[]`f^\bhiijikjihd``_[XYYVUXYYXWWWWVRMOUXZ\ZUSQNF@=9?AEA?CCAGG07iuS4/4?H>,/BCED518;BGHB@@>>>@@??@@?AJHA?<9971-0;EHG>AFB>ObdkpQ%'&&!.?:9?@;<;*#2AILMPMHLMQPNQRRSSRSUTSQMOMMLILOMPMJJKMNMIJG>1%*30$ 1P`__a`bdfhjntx~}~yJ-*511/.612;3!%'#(*/,*.)""""""##(&$(,))0AOSQONPO;A_r|ynJLWQRTWVSPOMPRTQH@><9>GJHFDEJDCanYT\YX\ZXZVVWVW[[YZXYX\\YZYYX[[JNz!05?J7)9>59FKJJII:,&$$%&$%&%$$&('(" &,2;H:( &))'"%#%*?MEHGGIIFEEJIHIIIHHKIJMLIJNJKLKKJKLLKNQOMNNQQRPORRLNRTQPQRPLCJ\i_JADEDDFEDGBBCEKUYSWXUPPUXVWLSzy\crMIPTPOMLMNPRLNRW[_ab\XXWUUTPSPOPRSW\_\^_`ekjgihcchhd\[XVUVWXVVTTWWVVQNKLRYYURRSLA>@==?@33@A=@948=BIF=9;:8346?GMOECBD?9G^gam`- "#$%(.6<3@=<@/!,BGKLNONJMOPPPQRROOPRRQNLKMOONOOOLMMMLKJJKJF:)+4:9'$#2Va\\]achhinpsx~~qA&+*&&+.-.2/#$))&*01/14.-+(&'((&'('(,01LROKKNRL>Ebt|~oJNZTSSRQQPPOPPRRJ@=:9?GIHIJFHAEgnUT_ZVVYZZ\ZYUVZVTZ[WX\[[]]\UYYFN"+:F@0/=?08KLJH@) !"!**)'&%&&$(('($)8D@.%&$  # $7JFEHJIHJJIIHHIJJJJLJKLKIJLJKKLKKLMOOPPMMNMMONNQRPPSSRQRRPMMHKWfaJ=DC@AEFCCD@=@IVXQWXVTSUVU\PSx}^\zvSJNQUTRMJJMPSSTWZ_cf`][ZVVVTTSQOOQSUX[YZbgedbcdehhc]_^[YYWUUUVUUXVSRVLHOVVTTMLJHECABCCA?>ACCAE8)_{`<:@=:978688867=;::;;;BDAINC7GVJ;6:EC3),,&340*0@IIKSSRRSUONRVXZ^^[VNE@@GMNXYPIKH@>BCB?>=<=>??C>??FD;8:979?DKLMNF@EG=7J`dbke9!###'08>?9>AD@@A@><=A>BDAAA@=@A?9648?MOONJJNIBBE?:I_fdhlK"# ")/=936<835>FLLKKLLIJLOOMMOPOONONMLNONLMNONNKKMMKKMMH=.!'7BC:'(+!5<% 6Yc^]_baefgkmpul6 #%'&&"#.588610674101110486596;KLNLMNNRP;A_s{vnLP\TTSSRRRQQQPSTL@;97?KOLIFFIBDemSP]ZZ[\ZXW]^YTX[ZZZ[\ZXY[ZY\a[GR.2+*.4<7;AIMD.$%'(&"!#&"" #0;B7*!!'$#" 'BGFDDGIIIIKKJJJKJIHKLJIJJHJKLLLLMNKKLKJMPPPNORQRSQSRSTSOORKGEPgdI>BDDBEEDDDCBBHSZWXUVZZWVYYTWwgTkxYIMOMOQQPOPQQQRTW\`bbaba[YYXSTRONNONKMRUTY\Yabdfecccc`b`WVXVXXVTVVUWQKIOSSSTKIA>>@EMIFCDEC@=?HD*N{f4#368;7326;ADOD4:6??4*((%'+.19AECMUSQSRTTPTTZfcUNVQDAGIFFGHFFGC??@=?A?>?=>=?BA=>BBB>99866:;95325777@KSMNKNNMQP>?]wx{qKOZUUTSRQQPMPQSRJBA<:?GGFGHLLCGgnTT\XTVWWTSTXYYYWVX^ZWXZ\ZW\Z^^IQ~%%'.+,;59DNJ3 &*,5FI;.(&$!$-BIOD6.%# ! $>FECBEHHHHIIIIJLLJIKLJIKLKIJLLLLNOLJLMLKMNMLNOPQRORQRTROORIHENffK<@EECDEDDFB@CLY\UWTTXYXY]WTVsiVivZINONNNPRRPNQRUW\_bdabeea^][XVTTRPOOKJOSSSVVY]`aabcdcb`^ZWVTRTTSTSRTSMJLPQPOGDB@@EJIHGDBABBBAGE.Dvm<7C?8:94,6;6135/,,*+*+/5:>C@<=?@ABCBE>;>@=:9NK>>HOE=BFJEE:3AH<9@<60-,*(,)(*1:BFJRPSN?DZZVUY]aefUJ@CGC>?AJMGDDA:==>>?@;30003579;?@AC?:9<>BFJLJFEAFHKGFIC?BFA>LQLMICGHEIMF?HHA9::7@A@91;E@@@:4111005999BPYY\abdaZ[]LLS]cca_PE@DFDB@DLOGACB;?BDD>53841021-.1.12248:9:LVQJHIJGHGHEFIBA@DD?J_ichgmZ-!! "($ "&-6=ELLLMONNPQQPOOOLNPPONMMPQQOOOPOQOMMNLJHB?.(7@HI;(-GD%3O_J%"%:Zb\]`eeiiimnou~T*(/+23(&+$'(!%(3<7+$"&,-+.5;?HKHFCJJBEflSS]XUWZZXVYXWWXZ[ZV\ZUX]\YW\`UBT"&+,11.9MTI@:7-,3/&)./1112573/00-0424467640.-.)/F: " ,CKFCEGGEEEGGFGKKHIKLMLJIILMMLJIIJKLNMKMOMNJKPONOOMQRPOPPOMIFHZfWD>GJFDEEEE@=>HX^ZUZ\YXXTNYYUh~|||aZjbLJQJJKMNOPPOPQSUY\]_^bgikjf`bdbbb`]\VUTQRTSPOQUVW_ic`dbXW[XVWVSRQPSOIGNTSJCDFGMSOFDMHFHGCDH>FXS+)_rTCDCIG<;@@,073345=9.,>B7=E:0+9GCDCBBFHIKNNURKQbjebimmlodF>BFTef]\TGAAABD@FMOF?AC>?BDDA813:<>=;::;<=<941/.4;=:>DC@HFBEIMPF>BGA98?HHFEFDJCDeoTO]ZXWWWZ]YVX\ZVVXYZYY[YX\YZ_ZFP~"#!!""%&+,14-.$  !""! """!!!#$!).(&12# ,>IDEGEGIHBHIHJKKLIHJLKIIKHIKMLJKNLKMNJJJHIKNMLKMNQNMQRNLMKKIEUgX@>CDBCCBBFB?@HV][WXWTUXWRQTYgvx|gYfbMKVPMNOKLOLNJJQUWZ^^_abcfikfecbaabb]ZWVVVSPQRTW[^`_acfe`[ZZWWVRRRQMNHCHTUKEGGNWVLFGJHLMIGE@BK]\3YsN0;C6.5:=C=4>A/0HM819NM9@F:/0:A?9CBGJC<::AFKKXkplhdhqmYG@A?CGN[_VL?=BA?CFFMKFJMGA@A>:9=??<99<>==?FCBCA==@<:6322343:7?IGECAGJA=SfabecllD!'* $#+/!,;>GJ@.!";K=% ;R\J)#!5T_]``^`hljkpspK&-+(,*(.*&*% *3:?8,  $%(/8HUPJOIONKGKSNCFFFGD@KCEejSS]YXZYXYXWWXZ[ZXWZZZYY[[Z]^]RAP{" %"$)+*! ,FJFFGDEJKHHJLJHIKHJKJIJJJIJLLJILQIJKKKLLNHKNPONNPNNOPPONOIIIESfZD/PS73CFHGMHIMGEHPOPLNI75D>22>B>>DB?CFENTVYTHB<::?EHC<@CFFCA@?@DIJE@@DCDEFEB?=68675387VjgbjgeldF..! !" +=DIMPNLONOPPQRRQTSSSSSRQRQLNSRNPRPMNMLKG<+.Ebs~z~jGLVSPSTPNONONNQSLA=:CECCCBCDA==ET[XWUVXVSTWWQP_mjoo[^\NMTPNNNLMOMJKLMORTT^]][]`eihfeghhfchie^\]\Y\YWVW[_cfhgcbcb][ZWTSSSQKHJRXQIIKNZ`UIFFJJLLHEDAIRaa=JeJ=JRHDIHGGGDFC>ADNGA@B?86@?30=A=?BH>/18=B:9/18=B:@YgYGEKMFCGHCCFAA>@CAAGL^bb_YK>;BE>>The\ceciqoe]I+#(&!$5>DINPKKRQPOOPRRQPQRQPOPQNORRQOOOOLKLLKMH8&$1?FGC4$*?G7"(BUZF% 7V^[]_bdimkmruswxA!)+',)(,)$&!'.2*" (.=D>1"!'0?WcZOLJNKINF:Cbu{~lJPZNOSSOPRQSQPRQF<;><@EGHGCGL@AdiQP\WWYWWXWUUXZZWVW[VUWZXXZX[^VEV2SA!$%',08<=;547=?>855-"!&)&,AFCCGGIJJFGHGGIJGIKKLKKKLIHIJKKJIJMMMMLJLMMMMLMNOOPPPNOOOLJIFN`\F=ABABB@@A@@AIVZTVWUTUXWSVTT_helr[WULKPPOONMMNNSPMMOQRRSVZ^afilkjigedddcffdccb`a^YVUVZ^adedbc_ZTXYXWXTNIGJQSMGHMR[YJEHIHMQOHDEGNWceE 8VM9BKD=DFE<@?;22<;::;=5-39B@609CC?EM;0@E98MG9BGCLJIRUMFHHFEGEEHKHEC@@CITbkikeWH<=Rechihebdo{sE"!"=J=+(6@EIMOLLRROMPPOORPRRQPOPRORVVQSRNQOMOLLNI6#!*5BIG?-0AC1 .GWYC#7U\Y\_ebgjkmrrpyq;$/+'+(&,'##" ##11)&)#"-:IL4  )1BZjdOFLILTH6Bbv{gGOXKPTQOQSRMMNPNG@A<9#&)+05;>>?DHFD>3($)/+/.09954**+-,)3GFDEFHHIHGHFEHJIGFGHHHGHHGJKJHILOIILNLLNLLMNNNMMMPNNOPPPPNIGFL^^F:BEB@?@CDB?=EU[VUVUSUXWSTUU^gek{v_SQKLNPPONNNNOKLORNHEGNPTW[^bdiiiihhhhffgigdce`][ZXW[__aefea]ZRRRRRQOLJDHSSHDIOW]THGMLLOOKFDHNT[chQ%-CD;C@6=?429?EE<JMDALRMF=58FKA=D<286/1;71H^`^N>HOKFEFELNHDH>7>GEBAHQZ^d\NDFKLOC83=KOF;5;FNNGA>LLKLSZXQ=AL[gfZNLOIMURPLDCC>>Sfddie_dinv~tM2;:'$7Y`F/,6BHIINPOOPNOQQNOQTRPPRSRQNRRPQOMNNLKKJHKH8%&0:DIG<(4B?+2KYW@"&?Z^[\^efghhlpsswk5#.*&+'&*&!$! *:2''-$"*?UN3!"&+Bay^FIMOSL9Car}~hGMSPUVQOONMMPQPME?=>:>DHGFCBJAEegPQZVVXWWWWUZ[XY[ZTZUUZ]YXZYY]SA[1K9$.120//149=BEGCDEFEB=:)($ !+78=>?A??CFEEECCDFIHEFKJHJIGHLKHHLIKLKJKMMJILMJLOMMMMNNOOPPNMOPQQQNGDEJ]bJ=CECCBBC>???GUXPTSTWUSTXTSQ[hhfo}{eRNMOOPQOMONMPPMMPPKKOOOPQTZafhgfhihd`gccffccffcbb_[\`bcfheb^]YRNQOKJNGFLUQHEGNY[QIJKJNNIFFGJNY^aj[06FG83?OM<:KLPUTTVOLMPTVUU[TIB;6?E<57<;0.11./*8MVO81CPMHIJHGME@GC77EHB==<50<84ALT]dbYOHYfc]XPXcJFD?>Sdcfke_dhjruP7;;6>>G_^A25?DIHGMQPOPQRQPQRSRQOOPPPPSRRUSOMQNOKLLKML=,%+5?FGB7%#8C:%6NZU>!(@Y\[]^eghhkorttvk5$,*&*'$)% ! "5;/%*2, *DYN5 ,Aiq^KJLOM9Cbs|}lKMPRTSQRQNMMPPNKGA?97=HJHFDDKADcfPU]YXZXXXWX]]XUY[ZUYZYWYZ[UW^UB];P4(0;CGGFFFFFEEEEECDDEA8+! !  !->FCEHE@EEDDDBCEJHGHIIHJHJKJJJKJHIIJLNMJKKLLJJKLNMLKLMOPQRQNNOPNNGEFHZcL?CDCEECBCCA?FV[TTSTVURTXUQP^lf_eu{fNIJMLPROMPOMPNKKORQONNOQQSUY[``bfijgdeaadgfefdcdeb^_c`beffc]XUPOQPJGHEIRTLGGHTYSGEFHLKNKFEFKT]__ke85PXLMRUVSRNOJHINQHDDGDDA?NXLGC7146=:<<=FQTIE91154,:FLGGLJCLRNCFJ@13<=9<<50'%!%-.+,3IO=147?EOR>;DEW[RML=((67<99DJJNOJIGC?=BKIC=?DA97ANOHF@2)5859??>;- $!!"+/+,6DKBINJEFKO@:9?FHJLEA?CJMIDHMWdebbWJDB?BViid`fjdgoosrW3)/7>47VbK=?DFIJKLJMVPMMQRQQSTQONPRQPRVTOOQONLPJHLJIMH;23;EH@5- #/;B3#9Q[S;!1GZ\^`^dgeeimprrwza.")*&*&$($3B@.&),/.%#!/LT:% !;MjrUEMG;Fdsz{gJPVTQLOVSMOMMNRRH?>>;>EDBEGHLAFhhNO[VVWVUVUTWXTRTVUTXZYZ\[XYX]R=[  F8/ADEKE?>ABA>@ABCCBBB<4-#!#" ,CK@AJHCGHFEGGFCIKHHJJGIHIKKJIJIJLLLLLLMKIJLMMKKNMMONLNMLNLLNPWUKDDFWbN=>DDCC@BBA@?FRUMTWSNQTUXTRTfn_ZbjoiUHJONOOMNQMKPQMNRQLKNMNNMQX]]accabffb\_a__beeda^`a]Z\`bdb`a_YSPMKLMLKEHRRHHPTUPJDCDGIMNNLJLU_`^`muM#/@BJ\R:>KSL=?J=0=D><8@F;5HTKEB+#3/0687DMEED><:7=CEQL>?D@8819EGFD<=?A@>;99;<4*$$$ !&*089579;@=BHKJEA>GLOIAAKVhndTUWNGBBD?=SihcfigcdltusM$#059;BOYMAFIIKMNMLLLSSQOQRPNOQQQRSQMRXUOQSPOSQLKNMJKIF?@ADI?4+  '1?A/&DU_U:";U\Y\_bchifdjttnv~[*%.(()%(*  4@?0).1.,/%%9OR4$ !!(6XtgONK8Cdt{xcCMURPOPQPNKRQNOPC;A;6;DGIHBAGAHdePT^VUZXTTWYWYXTWZUSUUUY\[[U[aPCf*1%*BKGAFC@ACDCA@BDCA=;:.'!" !#"'9FFGIDBFHECEFFFHJJIHGGHJIFEIKKLJHHJKJILKJJKKKKKJLMKLMKRONKLMMSVJCEDR_M<>EDBB@C?<<>FQUOTURSVUQRTQRgp^TZekgUJKOOLOMMPPOQQRPLKOPOMPQPQUZ[b_`fgb_a[\[YY\_`aca^^_^_a`ab`_[UPOOOONLJLLRRKLTVUOIEEFJMOLKKMQX_b`bozX""0?BFJE@B@99EEKXM57@A@HH@>ED969:=GMC5??<=<<<2;=<91%&" !"$'&$'05:>@@ABKVUJMajdh_PFHHECDCE?=SgecfifcdlsrI''--33?BFJJJHIKMNMLLMPPOPSSQQTSRSSQOMTSTTQMOQPNLLLMMJKIDFEFG;-%%-7>>- ,DS\Q7&@X]YZ\aecilggnsst|V% *(()%(*  3@<0)-1/.0-  ):PF."*CfkWNL=EbuxcEPVQOOPQQOMRSNMMD=A<8?FEFFBBE@JgeOSZXWUSWZXWWUWZWTVZURVYVV\Y[_O@b",?EBHHBBAABBA@CA?@A<0''!  #,8CHFBCGIFDEGHFIJHHIIFFGIJJIIIIJJIGGHJIIJKJJJKMKLMKLNMMLMMPPMRYJDGCM\MEC3,48;EMC3413@CBE?78@HI@EEE>>Tfcdfhfcdkr|wO)/>;5679?AFNLIKMMLLLMMORSTPQTROPTURRTUSQQQQOMMNNKINNFKLJLJGE8)!")4;?;,$5ERVJ3,I[\X[]bedijghnsst|R&())$()"2<:0),11003''CP@$#/MfcUH8De|ueFOVPONNPQPPNQPOOF==;8?FFEFBED=IhdNTZXWTTXZVXYVV\YTW[YWWYWWYYY^PA^%6CAAHBAAA?>==>?AA;2*%$(/5@FDCCFGECDFFDHIGGJJGIHHLLJHIJJJJKJIHIJKKKJIIKMKJLLKNMMMLOOMS[MFHCL[O=?DDAB@C@==@GRXUSWY]_ZUVXQOl~hTW`ebVLKLJMQOMOQOPLMQTTQNLLMMLMQW[\`cehlkhgd^XVWUSYYZ_a]\aa`bda_ZUSQNKJLPSILSXWWXVMIECFJMNMHFMV]`agkos}m7(GVUZRQ[VNLPVLEH@7=BCOQ?9EMDFQME;$=:&.;73AALL<7D?DCFHD@?D64238A@6.'$#!#""""  #%%+7?@ADBEIFCLPEBED=@VfcdfgecekqupR;AFJNKJKMMLLLMOONQUURRTQQUTQORSRPQSMJPNNOONPOJKLKMJGF9*! %.7<@9*)@ADF;A@?><<=>><7-$ "6BBEGEEEEEDDEFFFEFHIHIJKKIFHJJGJJJKLLKJLLLLKJIHLNLKMMLOKNNLONKS\OGFBKZO@?CBBC@A??>=COUPS^eeda_b^TPokSU^b_UMKKJOONORONROMOSTPOQOMJHJNSW[]afjnnlhfa\ZXUPRUVW[\[[^ad`\YWQOOMLKLNPLMRWZZXTJHFEHMOMMKLS\behiourwn?!=H?@COWG;@IKD7=KB0=22;?6=B0,5/5E>746;<9>51/+)("  ! "  !"#!!##!+&+105>?=DD:6=EGGHCIG@@?>>===<;5.'##!",AJFDDDGGFDDEFGFGGFGHHHHKIGFGIHGHIJJHIJLJJJKLLLKJIJJIJLJJNOMPNJR]REDBJUOB?AABD@@@A@>BOUPU`b]Y\bjj_[|qST`b^TLJJIRQOPRNLPOOPQQRRSTOJHIJMPU\achnmgffd`\ZUPSRPRVXX[[_b^WWUPLLKKKKKKKLOUYWPJKIHJNPOLNOSZ`dhlkqwponH%19?AE?7=ND0:GGPOKERG4J^]MKSb\OD4*0+$,68:7=IHIJ><@A>@HD5.169779;=7=F;4''6CQQG3!,CUYY_a`defghhjnqsuyJ '&!+)!"(2:>4,-11/.-5<9)!0IE+%2Tl_=C`t~}]CPTRRQOMMNOOKMSOD=>:9AGDDFCEF?HgdNRXVWXTSVYUUWYVTTWXVUZ[VU[XX]PA_=EBAA=><==<==81+!#$# !!!#!(@DEBABDFEDCCCDEFFGGEGIIGIFFHHGHJKIGGIJJIGHIJJKMNJIKLJKLJOQNJNLHR`UDCFHQPB@BABC@A@@??DQXVTZYSQS\gmddwSS]^ZSMLNNNPOORQPSNPQPQSTRTPNLJHJMPY^^cmmfkmkfa\WRTQOQUSV\]^_[URQMLJHHHIIHGKPTUQJFMHHOSPLKLOU[_bfknpulkpR'8HNUQ99UeZBJ_]YYXY\ZVVVUPTOMRTK9//'$-//677HLJJFMJE?;;:;FB90$!0?FQOA-3NZZY^_\cfggghknqswwJ%$ ,* (4=>2*.20/1028;3# $;G8#!)>\fBAax^BNSKOSUSOLKMKNSOE><:9AGDCFDEE?KhbLSWVWWTUWVVWWXXUTXWZZXYYYYVY^NAc =A?>;:=8=<<<:1#!! !#!  (>NE@DHGECFEDDDDFHGDDGIIIJCGJJIIJKIJJJIHHHHIJIHHJMIKKKMLJKLNKIPOJSdXEEIHOQA@CBAB@B<;;>DOXXQUWWVSV`f_cwOO``[RLKLNOSQNPQOOOOPQRSTUQPPPLIJNRTW\cimnjmnkgdb`QSSRSUVZZXWVRONLKHFFHHGEHNTTQMLONFFRVNHIQTZadfjnporjjtY/$NcVMWSPWSMPWSJHIF@ICBLOG<=0 +$"3.!9MJD>IOKMNHIRVWSTQGGIA:53;29M7+5.+=E?A928@>>9, ,("'/*+,+))*,&$""" !"!!$&6IL>?TdggfdcceiktrT87;9?NHD=4,"#6EIPL>*!9Ua\W\]\bechllhhpxyxM$# ,*!(4=>0)/4103334660$#-FFFHDGE>Lh`KV[UTXYWTPVXWUUVVVW[YTTY[\RX_PCh(?A=9::9<;84-#!!!" "! !#$" '#"!"5FFDEFGEEFEDGGCFJFHHHHHIIIHHHIJJIGHHIKJGGHLLIIKKJMMJIKMMKKKLLJNMKSeYFFKILL=ADDBAABA<CUL>=ELE9CJA7;E?67HN@2@RM@C3 '%0'+GME>6EFKSULIRPI>55995011-/@JF@ELEFH><561/7>@A8% *&%) +, $/2.*//++(&& !&%"!!6EH>BYijceecbelqxsM*/<>@L>9@HKLQRMJLONLJNOMOUUONRSRRQQRSSQPNKKNNNLKKMNMLLOOLJJJJE>99>CB;/$ !&:JHPR>%$1@O[YZ^a``adehkoquxuL !(# #'2=81-/341/12873;8#$%,=>, $.AXTD^~aCMSMQQMMQQLQONQPG>><=ADDEEA?C>Lg_MUVUY\ZWWVQTVVWXVSVUVZZVUV[XZLBf*=<;=>76?95,# #"$"!#&'$!$% *;DHGGECBCDEFCBEGECCHGFEEEEFIHFFGHIHJHFHJJIIHJKHFJLHJNKFHOPKJLLKNMIPi\ECJIKJ?@BBABDF@;;=BOZYUVX]^XZei]bXM[aWMLLMLORPPRRPRSTRPPRSSWTRQPMLNHKOSX]cgmoponnljg`XSQQU[][VSPNLJIIFFHHFGHMQOGEHLMPONOLKQSYagijmqrpmlkxm=1>34??BA7:G@/8ECHTMGQJ@L[]XPVd^HF>&"(1+,026BE:3DONLJ@2>@<2/598;ACAACHLDGRF8>>5BXhgfggdacinusT20;?BBAAFMIFMTOKLMMMNIOQOPSROPOOQTUTRQRSQLKNPNMLLMNMMMOOMKLKJLC?CC?:6-!$+:IJPS@)(6HW\ZY[]]_acfijlptwvP "'#"%'0:9/),1222*286497.  ''1;7#"!)>NTKa~cAKVPRQOMNMLJLORNB:<89?EEEDADI@JgcNPZVVWUTUTTWXXXWWVWVVYZWWYVY]MBg.@<9=?96;)&!  #%""""!"$%$#"+>JJHEFECCDDBBFDCEDBEEEGGGFFEJHFEEEFFJIHHGFGHIHIIFGJIKIIKLKLNJLNLNMHNe]GCLKKFBAA@@ABCA=;CZ_ZZ[\YRS[ZQTWOQWO@A4'(*462+-:CB2;@FMH:14=A?BJKE>JMIF?:=5>QNFX_P?IK?0*097@B<+%*"$# *'$# "$ "#!'+*.429HJ?AWfffggdbcintqW83IfcORWSSVVVWUXWWVUUVXZXWXXVVXTX]NFn1D?87;;5.  "" "$&!"""""#$"#0BIHGFBDDDCEDCBDDEIKGBHHFEDDEFHHIIJJJJJKKKHGGHGLKGIKJHIJJJIKLNLMOMNNKOgdLBHIHD@@???@??C?;:APXUVXWRPValh\c^Ocj]QNJJKNOOQRSSUPQPOQRQSTSSSQONOOOOOQU[_ccgnponpomicZUX_^][YWVTSRPOQQLKRXWTNGIOQVTQOOPRVZdmqstrommkkjsoN3QYTV^bXVYRUWSRMFHEBCACNK?EJ=;FIA@:%&)+540.027??=59IJDFKJFDHKC65@A;>FHGMU`^[eaKIEA>80**29AWffbdeecfkpvoT85?B@JF53EMMPNOPQOLLNLKMMLPRNNPPONMPRRPPPOOPNOMKLMNLIOOMMMONLIKLKIG=0'!$/8CJJKRL?2-5:513/563,#&"!*>A*#" +8GR`q{s]COTPPPONNNMOMMPPF<;96;DEDDFEE>>>@@A@A>::CRZYY]]UT^jnn^``MamaQMJKMMMOPQRTTQPRSSRSQSUUSQPPOONMLMPUY_^bilkknmnplcZ[`_ab^YVVWWUTUUOOUYWQLKLS\YRPSSSV[djoprttqlnnomnkQ#*L\QFIMEOMB@DIMA@ANK+'GPIMONONLILONPSQMOSRPPPQQQPOOPQPLLOPNMNNONMLLMNNPQOKLOOJIJA0(',4?JNJNRNDCIRXXY[[]_ce`gkgflrtz|l)%&&,&"/A>3/6:537065&'&!#2C5&!#!'4=M`p}u[BOSORRMMOOKKMOQMB;=;8=EFCEFIFA@??>?AB@=:=FPVWUZac`cecaVZ|eMbuhOIKMNPOQSRTVUSORXVSSSTWWTQRQPMMMMMPTWZ[^befhilorqi`]`cghd\WWZ[YUWYVTW^YNKNQU_[RQVXY^dlmnnopqpnqqsoliW+1A;2?G;CF@DH?7:LHOMBLSDENVWZ_a_RDFA*#*11.*&+25*-@F75@AKE?;8;EOWZbd][\VC<>C>HSF<>DOO<;JLMMMNNOMJJMONOPNMOQPQRQONNOONONLMOONPRRONMNMNMMNPPOPNLJIJC8-05LULQQLLRRMNNNOMC=>:9@HGEDCHG?@A>;=@BABCCHJHHID>@RuiP[tjPHHIKOMOQPQTROQTTTUURSWYXUSQONNOONORTVXYZ]befikpqmedfmoojc][\YXVVYYWYUSOJKQ\g_USWY`jnmnqttpkinqpplhh^5,@=5ANM:29R`F9QZZTVa_WYWXWUSQMIPJHF4!#+0,/.%%18%0MF56@>CILKKPVVUQKFECA<:9>>I\XMW`S:;JNFLNIK]Q*#2+()"/:'(,$5DG=@WffcfhgedhksrP7ACAB@>>??<:=>9667688587.4@WufQ[rhQJHLUPNPRPRTROPPRURPSXYXVRQRSMMNMKJJKSUVUX`dddgkonjjmvvtpid`^X\\Z\]]`ZTPLMYegaYUVYfruxsopommolokkfcgb<0EHKQTZ[NQW[]SMSTMHID>A?IOG?AB=>HHLB)!%#$..,1)#(>OG::3:CIKIB;HJ>3:CB>IUUX[WWPHITVHDMSLJH@IRTQQPMKMRXZVY]_\Z\^__afknoqs}~}?($&/+#!$'$"!&7C4" &($"")67' &*.0RnxS>MQOOOOONNOKLLNK@:<8:?BBDEBEB:Mk_JSXUUVTTVXVWUSTVVTYXVUUVWXVWXICl$%000.&%*/21230+!!# $%"!"$,6?HMJCHGFEEDDDAABCCCCCCCBBBDFGGFEEEEFIFFFFFGGFHHHIIGGFFGHGHJJJIIJKMNLKKKMIHKLNdmX@FQNC:?@?AAAA><:7<<;:86549BRm~xiXWloYLKHKRPNORROLOQONQSTVSUXXURQQOPOLKMLHNPOMPX^^a`dkomlmuropqng`\XVXZ[^b_WRSW`hjeZT[fnppsojiousnmngadefjG8_^WZMEMJLMH??BNIB?<55>ECLI>AC>1;PME@,!-.#$*/15+5LI@A5+4CHD7-:<=>FQY[a]ZVJ;7@UficfgebchmxnN;DG@BKC16JNJJJNPOOONLLMNQRSRQSPPOLLMLMLNRPKLQQQPNLKLMOQNJLMNQOMNMKJJIECGMMPRNKNQRRSVX\]\\^^]_fceknmnrz}}J""*0'$252.*%7VI !"'# %).-%#+/+FyQ=MUJMPQOMMNMKMNJA=>87@FDCFDII=Lh]KP^WTTRSVVUWWUUVVTTXXVVVWYUW\L@f+#!../-"'/430.05:<83*!!!! "&)+5A@>?@@A@=<<<;:;;=@?=;<=DGMaqqgYYou[GILPPOOPPRRSPMNPQPRUUVWXYXUROPNMLLKIJLLKMSUUUVZadglrqnkklmlj_][ZXX\bd_XRTakl`[Y]ekllmmllnqplljc_a`ahM 7YI6@=4CA>SX>7EMM?7BH<2HB/0GVN;6F[WHF:',+"%+/9@07QF39B.+4=DA?CDLY`_XUEA?A?;@UfhfggeccgjtmM9GO=/::/6INLMNPPOPQOLLLMORTTTMNOPQPOONPQRPNNPMMMMMLLKOQNJKMNQQNLKIKNOLHIQTPNRQOMLOTX[[]]]__^_bdinppop|R%#  $1AG<970.,"+T[3"''&#&..,.4/?myGLg]LRVSUXWVUSUVWXXWWWUTYZVVYXRTXJ@g'1!$,02. !/242248;@DC@;5.)%#$'()/:BDEIKIGGHGJHEDEECACCCCBBAACBBBCCDDDBCEFCCEGFGGGGGHFGGFFHIHKJJJIGGHLIHKKIHJKMPLLNIF`m]CDRQC>AA?@?>>A=;;=>?A?ACEEDDDHMS`jiaTQes`JHLPQRSQPOSVSQQNLQVSTUUVWXUPQOOONLKLKMNMNOPNPQUXY[biiknprqpoib\YYZ\][\\Z\dhefc_\_ejkjorqoppnnibacacmV%0TI2684BBLER]HPRFGRZZJBJVSDCD/! )+('+37=,6QA*3@@6/:>=NOQROOLD944:CHDBCBCCDHHJNOLMJIRI,#+.!$)&++/>+2;#5EI>@VghhgfdddfhtoTBHF72=?69JMKIQQPOPRQNLLLLOSSPPRQPRPMOJKNOOOPRQPOOONMKMPPNNOOPMLNONNNLPPNSYSNTPNKMRWYZZ]]^a`_`acefhmrv{~Z '&%,,,.6B@:621=A5'FW@ &0* !072++2B`vwyFAPTMOQQPONNLKNQMC==;7=EFFGBCF>Mh[MVXTUWVTTSVVWYXUTUUXWVXUSYSVZK@d.1&,/2+ (/2005=@??CCCCA?<977:<>BHKLHJJIFGGHHFCDEGECBBCCCCBAEDCBBCDEDEEDEFFDFGHGFHHHFHGEEHHGJIGFHLIDIJIGHKLLHFILMNJJ]l^B@PSD=AA@@?==@<9<@A@?>;9:==848HUadb_VQ\mgOFGKQSTTQQSUROOPNNPPORSSTVWUSQQSQNNQNOONNONNNPRSRRUYdgjllmnpof\YZ\\ZYWY^bgjhffa]aipqkpsqooonle_bfber_, +QU?765=II;-A_\>31:BAGE<:AD@B?>AA>;<>CEHMKIOK>85=LH4-43,(%#)/+$%%.=-+8%6EH>@WgghfdddfghwrW?=83A@2"$5<808EA* !,.)(3:2)%,4BV[`xvDDPTOPQQPOONNLMOKB>?86@@=>??@@;8;?>;86201441-:O]a_^a\SXhcOFJNMORTUUUUPQPQRQPRPRSRSVYZVUVWUQRVQONMMMNPLLMMNOQQTY`cddgilh`ZXY[\^XX\bjoohigehnpnkmljjkkha\Y^dadqe4"HZOGHIHQZKCNRQFNMGF>9?@?@?OQ=BD@7DJEI;!&.)$(,0<7.>M:).*(?937;@ACB?;;<43566457BIGCKMF?91-(6PJ3,3113(")*,+(#!)/&!.&7EH=AWgfedccehjkxrN.0744DA67FOQMJNPNNPSTOLJIMQQONNQSQQPMQONOMIIKOOPOONOOOONLKNQQOMMMLMONNORRRVUNNPQSTVZ]Z]^_bbabcdhlkilrv|i+%$":73.+)./2AJRcwc;EPRPPPPPONNNLMOKB=>76>ECBEBCC:Mi[LWZUTVVVYYWVTTVYXVSSVWVWYWXYZL?Z-1)(10+ "/0/6=>@DEDBC@ACDEEEDGDDHHFFJIFEIJIEDFGGFCBCDCBBBBCCCABCDDDCCGCBEFEDFFFGHGEFIHHGGFFFGIHGHIJIHHHIJJKMOLMNIIMJHXjeKBQWG?@?@@<9;=<:977643468F\fd\Z\UMUbZHHLNLNPTUVTRQURNOPNNTTSSSUWYXYZZWUVYWTRSRQRUNNLJKOPOPV^ehikljf_YWYZZ[YXW[flionkijkkilkighkhd\[[`gefof= ?ddPMZN=GFBJC<;BOL:;DB78GBIP=>IJ@DHJRK)!/1/-013-=L7&-'%$)6<7>@@@A?:426421/147@E:-6IE3499'2SI+/3131%"'&&)*())$%$"",- 7EG=AYgfccccdgkmwnJ/4833?=49FNPMFLPOMOSURQLHJPQOQPRROOOLNONMLMMLKMOPONMNLLLJIKONJJMOOPPOOKOTSTUQNRVXWXY[\^]^`a`b`_agiinvwp4!>?;9;NUE8BNM?<:8650146762//3.$/GI72MfYJTXTVXWUURRSRRSTVWYUWWTVYVUTUKARu*5/-2%)53493ABDFGGGGFHGDDGHFIGHIGDFKEEDDDDEEEDCBAAAAABDEFFFECBBEGFCBEEFHFDEIHEFJJGGIDIKFDHIGFJKHGJJHHJMJIKJKWhfOANXJ<@?=>??@@;8;??:6/.,,.4;@Vss^W\ZQORNIJIJJKMOQSTTSQRQNQSQXTTVUQPRV[]YVWYYZXY\ZURSSZ_\YXXVSSSTX^cfddaYTTVVSRTTRWag_cilhbdicegffe`ZW^`bfe``dH)WhH0351EH9D@:7@DVE+'0*+2002.275.()--(-'(3108EF=ADCC@BBAADEDEDEIHEEHFEDCEIIFDHHGHIIKIHIIFGIFGHKLKHIKJKLKLMJGXhiWAJ\M<=A>=@>?@<79@<1-/-.5>@EP]uxaVSNLMMLIGIJJKMQSSTUVSOPSPNPSUUVWTRVSV[]][XV[_^ZZZYZ^\[\[ZY[YWVVXZ]^adc^YVTSRVWPNW``]`cb`bcabegfda]Y^bdcbb`\fT((GaV@=::;CH:2>7FUB*0/+)++)(+/1002.'#(+))/227FJ>=UhgddcbbfkpxoK1:A95.'&4HNLNMNNNNNNORUOJMLKNNPTTOMQSSMLNNNMKLKJJJKLMMMNNLJKNNOONNPSUWSQTZ][XXYZZZZ\^]\]`cdca`cgjlmoquq1 ;E-&50$/5/'-0% +0/04@NM>@OLGF7KsqZ>@<;??A=<89<945138DTYVVe~cUQLJJJIIHIJJKMQSSTUVSQPPTVSSSSSSRRUVUX]\XX\XY]_\[]_\\]^\Z[^_\YWWXZ\_`^XSQRRTSTQLOZb_]_b``cffffc^[[\`cecbb_[eX,#E`[JHGIGDEGJNLPQKJLMPPLIIKNLILIRSKIEB6*%! "%0/86%"*-.2('-$#5D@3--.21*/-+12+&&.735HH,5;?9@L<$01/--,*+(,/.+***,,++.2317EJ>=ThgbcdddfjmuoN4:=0)$"$2GQNMLLLLMMMMURNMMKLQPNORPNOPMOSSNNOOJMNMIIJMLMNNMLKJIKLMORTTWUSUY\]\ZYXXY[]_^^^^_`abbehijmquvp/5@2++  $/:1$&)"'7842106==9CBDE14Xz~|H?ONMSPQQOMLMNNLMNJA<=88>DDDEFCD>Sj[LV^SV[XROUTTUVVTQRWVTSTVWYVT[ZCBf!02 "''5604889>BBAABBCC?@BBDFCFEFGGEEFHIHGFFECDCBCDDB@CBCCABC@CCCCCCCCBDEEEEFEEFHJHEEFGFIJFDHIGIHEGIHFHGHIJHHHIJKJKKJFTek[CGYM<=A>=@>?<;99<=?DCDKTTFCNlhUQMPNLKJIHGLLNRTUUVUQQTTTUTTUWWVVWUUWZYZZYWXZ\[[__YWY]``_`bbb`^][[Zba[SNOTWQPSTMKU^`^adbacdgec_YVZ`befcba^Zd_5>VVONID@?=;;<>E@;DMNMTNHFJJE@?FK;*+4=9.+%$,:8"%.23,-()=??/(043.&'&)12+%"1;55DE4-0;>DM?()+,+-,-/*,.-++/4.+*,.-,,8DI>=SggaceffgikupO354%#$(5JUROKLLLMMMNRQPLJMOLONPRONOPPPPOQUSLLLMLKJJIJMNKIIKKLLOSRNQWTUXZ[\\\YWVVY[\\\^`a`_`aecdfloqpxm,2?5,"$0:1##'!5C7002-'(),SlZIPUSXWWYUPVVTSWTPRTVSRUUVYTTZXH@["0/!&05313448>@??BCA@BBCECEFFEEFGFEDEFHHGFFEBDEEEDCBCBDB@@@?BC??BB?@DFEEFEBBEGGEBBEHHGHFGHGEHIHDFJIFGGHIKJIHJIJIIIKIFUgo`EFVL;=A?>@>><968=CJRPKIPNBAQmnYTOKKJIIIIILLNRTTUVWTQSUSQUQSVWUWWRWUUZ][WVUXYYYXX]]\]_``abbddca`][[\ZTOOQRMOSSNMU[Z]`bbefdba_[XX]cdgfcaa^Zdg?5GLIF?<:>A<64=>64@NPFCLHAA:,')68)!"'68/00&+3'$+0104(3@@;-*2550""#*00*&!155==796IKHKK>0:Lf{|B?QMPQLLOQPMKKMNLLNI@<=68?FFDDCGEA=<@>@?;::;=DIMPNLQTY_pu^WNGIJIHHJLJJMQSSSUVWURTTRSVUVUTVYVVRSX]\ZYX[]\\Z[ac`^]^^addfgcaa`_[\[VQPQQQOPQNOX_Z[]^_dge_^[XY^bdegfcaa_\ahI/AIE=:BAQegeeeccejmxqK'$$*&'4FKKNLLMNNOPPOOPPQNLMLJKMOQQNOSTQOOQRNNNLKKMNKLNNNLNQNOONQVVRWWYYYYWWTUWXYY[[__`aacdeabdeimswyg'.B- ,92# $!!  >@>>@=<<;DS_Z[XVVUW]u}fZKLMMKHGHJKLNRTTUVVTVXUUUSXVTSRUWVSUWY[][XXY[\[]^[^_`a_^_cfjkhddb``a]VQQTUTMNSQOWa_\^bccdd`]YW[beeegfbaa`^^eO!+AIB;57:7/++-117B94;.76*.02D6/.&&:?5.-3301)"!&/53%8,%9B=;3+)3:-$,0,,-,/14:CJKIHHEENL7.+/022.))$&)*,---/--01/+*8BF?>QdhgfdbadinupM)$#*&'6HMNTJJKLMNOOOSROPRPONIJPQNOQQSRRSSPPRQONNOPPMJIKMMMOOMOTSPRYWWXXYWURUVWXXY[^]^_abcdd`bcegjnqxe&+?- '61$"&"1IJ@;4379?GKIIJIFEFF>537DRcg?FLPQPRQPNLMOPMJMLNQLB<<::@DCBCCFB:QgVGUTSURRYVQSQTUQPQQOQVXTSSQZXXUTHCe1.,10121/2578:>@ADDDDBDDCBACEEEGGFEGIHHGFFGHG@CEDA@AC>=@CBABAACB@@DECBFFCBFGFEDFHIHHJGFHHGGIHHCDGIGHHIFEGIIHGJJIHIJJGR`ibJHYT:0'(16-%*1/*/77PchefedcehltqO,%###&4FLMPJJKLMNOORNOQPNOPMLORPMORNRRRSQPRTQNNQQPMNOOMKKOSOPPRVYYWUVXYXVSQUUVWXZ[\\_bccbcd_`bejllkv}e&'7( &41&$($0JRNKMEFLMLKGIKIDDHKJ@=;0387DTbdm}}c?ANMNQNOOLMMLKKKMMLMOJA<=88>DDDEFE@9Rm[LXXQTVTRPSRQSUUSRRSUWWUUWXWTYVXL>T /++22252142/39@DCA@ACEBBBACEDDEEEDCEHFEEFEDCDACEDBABC<=<>A@@CCBBDDCBCBCCCCDFGFFEDGJIFIIFGJHFHGGJIDEGFJHGGHHIKKKIHIKJHR_gaIFWR<=A>>@>><8:<:;??FC=98?WsuzjRJIIIIJJJIJLPRRSTSVUTVTST[[XX[XUXZXWXZ[ZXWVTV]^\\Z[]aba`abghdabbaab`YSRRRSTUPJNY`a``__bb]YWY`fhggdfeaaccadf^4!/%$;1&5(1?.0--?:'.2&)-0/-3=3-;<) *9/*//+-.20% "$)-/'49*,AEFHC92'!),)',.,+)*/-,.1/)')$,0/.10.8AE?>PchdeffeegivsQ-$"#'+7GPNNKLLMOPPQPNRSOQSMMNONMOOLLPPOPPPSQQPPONMLOQQNLMOORSTVXYWUVWWWVUTTWWX[]^]\_bdda`cgbdfijloqu|d'"-!(84% $#!$DTKELJFINJEHLHGEHKJGHIDI;0644-887BFCFWaafzwuw{{`9ADBAEC@CCCDEDDFIHGGGGGFDJKIGIHGHJHGGFEEFIJJGHKKHGKKHGJIESflfPFTO<;?>=@????88>4##3YtrpTMLLKIIHFCGMPQRUWVVUTTUWYYTRTUUW[WWYXRRWYXXWVWYZYXY[\]_abefeccddbda]XUUTNWQLMQQV^ac`[^`][YZ^cddehfccffa`bacbA '3$"9/.2&9;)**.?6'10*,%//(5:.*/94%%20(+20-1442+$ "%'(4@7%(9@8..1+(.4=>><989;CHKT\ekwyQ:=IMNNMLLMNOMLLMMMLLLNMGA>?99?DCBBBHB8SkVITVRWZVWWQOOUVSSRSRRUURRUUTWWTTUK=l"0, .4122222333:@B@@ABCCBBDCBCEEDDEGIIIHFDCDDCACCCBA@@@>@BBAABD@BBBBDCBCBDGGFDCAFJJGFHLIKIHIHEFIFEFGGFEJIGHHHHHGJKHGJIFSaghTHTR>;?@?A?@?;46<0 &A_sq~w]PLIIJJIGHJLMNPSVVWXXWWXZTSSVXVUUW^ZSUWWZ[YXXXWX[^\ZZ\_abefgfdcdfc_^]VSTVRUVVTPT`b^]]\\ZTUZ`cddddegihc__b_cdG  ,!1,/3(56*'-180)2*00!*5,.3%)+)%%-5.+-/-/32032*$$&("%28/-9A:48??>><999878:;;7200/+)**'$&*,*(.-*+,)(+%&--&'.0,(&&')+6AC?>LbcbbdfdcgmtpO/)$" #*9JOOQPNLKMOPPRONNNQRONMMKKMONTQOPSTRPPNQTSRQNQPPRQPQTROOTURQRSVURSTTTYXWXZ\_`_`aabdcagddhjkmmv~Z#):5&#*)4HMLLKIJJJKJKJJJJJJIIIIKKIHGFEDCBBAA@@@<;:9888958;=AFHHQ\hjiuzq^RC=@GKLMMMMLLMNLNPPNLLNGILMHA==67=CCCDDGB8SiVLZYQSVSTXVTUXTRTSQUVSSWWSTSWWTUVMASu!.)!06444333455:?BBB@@CCCDEEDEGDDDEFGGFEDDFGGEC@AAA@@@A@@@AAAA@@BCAACEBFEDFGFDCGGHIJJHFFGHGIGFGKHFFGFEDFEEFGHHIILMKKMMJW`ejYGPP>8=?>>=?=:6<@/ $''''0B\oq}hUMGGKLJIIJKMOPRSUWYXUSSTYWUTTTSS\_XV]]Y\]ZYYYXWYZZ[^`a_]ccdedbcgfbb_WSUXUXYXUQU_ebcb]YWSV]defffdafigdcef\bhO!&#!,#0/,;9.,15;/'/+67!%5) '#43 4=3+-/,+14/0671+)(%# %,/8@A?:9::Ncdda`abbfkytT2,)%)'*.:IMKMNLJIKLMMMNPOMOSSMNMNPONPLORQONQSQSSPOTUOQPQRRPRUSSUTQTWSSTUTQRSSWVX[]\^```_^adfegbcgjkort}~Z# '43%"+-"-IULJNMIHIKLLKJHJJJJIIIIJJJIIHHHIIIIHHHHDEEEDBA@=;;<;99:8:?>9=@:=;:;AEIJLMMLLKMNNMLJKKLLKLMLF?;<66=CDCEEC@;ViRHUXPPTQPRRTRTTSUSSVVWVUUUUSVWTVWQGFd!-'"16534454445;?BDB??C@ACDCCDFBEFHGGGIFEDDDDCABBBA@@?????@BCBBBFDABCABGFFEDDEGGHIJIIGFHJJJIIIJIGGGGFFGGIHFGKKHGJJJJLKIWael[GNO>6;@=<;>?=7;=/&/6965:GYkn}m[RHGKKJKJIIJNQRSTWYYVTTTSWYVPOTXTW[\ZX[^^][Z\^\XXYZ\^___dddfggghddc^Y[][YZXVTQS[`b`\ZWSTZ`egffgghkjfdec^]biU)$('*$0)+<7*20-0,*2/3;+*80(.5=?:@:7:;;<89?EDCDDFA:UfRHUXQPRQSWXSRVVUVUTWTUVUWWTSVVUWYTN@ABB>@EABCCDDEFBDFFEDEFGFFEEEDCCCBBAA@@;>AA?>@B@CCBCDEGEDCDFGFCEHKJGEFGKJJHFEGGGEEEEDDFFGGDFIJGFHIHIJJHR`fl\IPSA8>B>=>??=67:0%)/-'):JWck{maYMHIIILJIIKNQQQRUYZXVUTXVRPQTVVTUZZX]_Y]\ZY[_^Z[[[ZY[_bcefghjjigggb\\^ZW[[XUPRXZ\XSUVVV\_cffddfjlkfdda\_biY0!*)&",3,+0,(60#&5;17A<9?>;<>=;:=A<4,-,+,.//,../372)$!!#!#.:CCBCCG@;WjVNXWSRQPQSRQUYROTSNSUTSWVUWSUVUWXVSEBg".&&2322210/269?AABBA@D@AAABCCCFGGGEEDECCDEEECBCBBBBBBA@BDCA@ABEBDE@AEE=BGFCBDGHHGHHHGFLKJHDEHIKHFGHGFEGFEGHIIIHIIJIJIIRbgj[JQR@8>B<=>=>>77>4"3ESbgxme`RIIIHLJJKMPPONRUY\[ZWVQV[\XUTVWY\[X[^^^ZYY[\^a]_`^[Z\_`ehggijjmkki_XWXZ[XVUSRTY[[TNSZ[^`chhfdedikhcaab`ag]7'*&(+02,#%/A=515=@;6:;9:<;62;>9:@;.,,*+-,+0.../11.**#"%# %/881*((#  !"""%((')-.+,-,+,.,()-))//)+1-*+*()-3,(*+'$%3CE>=PfdfcbcddhmvrV5*.-*)116HNJLPNKJKMMMLMKKNPNMLOOOONLLPPOOOOOOPOONNSVSQPPRQPQTTTRRSNMRSRSUSTX[XYYXZ]\Z_``^_bdefhhedkpooz~_'$! (34(!!#2BNPKHLJGJLKIIHJKLJJJJIIIIJJJJJKKKHHHIIIJJKKLLKKJINLKLKIHIJEINLMLGEKPPKIMRLMMMKLLNNNLKJJKKJKMLF>::45;AAABBE>;YjVLTSSTUWWVTQTWRPUWUPTUUSSUXTUUUVWWVL?O|+$(5444431149>@ABCDCBCCCCBDFECCDEDDDFGGGFEDDCCDCBBBB@?@@AAAAAACADC>@EDCBABEHGDEFGGHIKLHGIHDEIIFDBDFFEEIGFFGFFFGGGHHGFFUdfi\JOQ?8?@:=?;@@86<4# &$':HUedtoheVIIJIKMLJKLOQRNORUXYYYSUX\ZWVYZWVXZ[\\^\\]^^`d`aa_[Z[]beijiijlkikjd\ZYZYTRTTQPXXXSNRZ]bbdfhhfefhjhc^^a_^ea@(33:89>:26>><=<:76:99<==<99==95331.,,**-+*/0/01/,**/-'#$%$"&'"!" $$#""#&(*,/-++-/./.+,..*,,*,330/,+)(''((! ! 1BE<;PfcaacdcbfksoX9/54.*437JQMOSQNMNOPOJNOMNNNPSQQQONNNNMMNOOOORKLSSPQTPOOPPOPSQOQWTKLTSTTVWUTWYVSSWZZYZ^```bdecdfffkqqo{a(%!(14' "$/;VdOIRVTRRSRRSROSTSTVYUQSUTVXTUUUUUUVXWF6T+$*5321232249=BCAACDCDDDBBDFEBCEGECCFIDEEEEDCCCBAABBA@?BDB?=>@@CC@>ADDCEFEFHHGGHHHGHKMJIKJFFIIHFFFFEEFHJIFGJJGJIIJIHGGUbbg^MQSA:BB;@B<:=87;2$!()#)@NS]bqz}rigWIJLKKKJJKMPSTPOOQUY[\USUXXTSUUWYYY]_[Z[\[]__]^__^\[[[cbdhihhijjjhec]TQUWUSPLLXTNNUZ]`ccbadgfdc`affcbe^\ddE ,>CB>888:;3<9637>;:=;??;3.41/(!#*)#+-*)-,*..,-11-,.,1/&""" & #')'&&'.+,020./00.+,//,*,.,)%%'&'&" !"!$*-2CF==Rhd^_ceb_cjoseOGJD87?99IOKNROMLLMNNLPONQQPQSNQTQNNMNOOOOOOONRRPSSPNNPPNNPQQOPTSNOSRNPQUYWSVWUVZ]\[[]ab`_`cfabfhgimnq}d,&")14*'*(2AQOMJIMNKIJKLLKJIGHHIJKKLHHHGGFFEHIIJJKKLMLJHIJLMJNMIIMMILPJFMOJHLJHIKMMMMNNMLLMNNOOMKKKLIKMMG@<<;;@ECAA@@;b!+ *000210/26;>BBCDDCBACDEFFECBFFGFECBAEEEDDDCCCCA@AB@=>@A??ADD?@DDACFDBCDEFGHHJJIGEFIKJHHIIHHIMJHGGFGIFEGGEGIEEJKHHJJGRafj_JHM=9>=>C=7;B1#%(AOW]_nwswnlm]JFHILLKKKKOQPVPOTXVWZZ[YXZWSSVPU\ZY^`^^__][[^Z\_a`][Z\`bfkkgfohffb]YVUVVTROMLSNMV]\\a_b`_cffhiecdfea^^bdiP$(AIA85;?<:;?@@>:86510/..+'$$!"&)+&()((+./,01/153--/10-#)(%" !%(('(*.-)%-/.*,21*-/-)),+'$"  ! ##"&#)''*-3;=:@H@:Qhfbdfc__dirncZ^f_PWUKGPPHILMNPPPMJRNJLPMLQPNNQRPMKMNONNOOMNQSSRQSTLKLNNMNPOPPPQTTRPOSVVSU[YTWZUW^^^[`d__dcabdgjkkknvyq4" ""()3-'%&'0@OOKIKJIKKJIGGHIKKIHIIIHIDGJIIIJIHIIJJIIHHNMGHKJILMNMLKLMMLJKLLJHHMNJJMNLLLMNLMMJLOOMMLKMKIKLF><<77:@CCBAD?=ZjSHTSTVUQPSSQSUVTSRSRQRTVUUUVRTXWUTSTTN@;_!*+100432359=?@@@A@@@@ABDEEEDCADGGFDCCEEDDCCCC@@??@BA?>ABB@A@?AABBBDEBDCBEGIGEHHHIJKJIIJIGGHHEEFDEGFDHGGGIIGFGFGHJKJIHO_be_OMN@<@>;=;;A>89?2#$(BPUW]jrosnhi[LIJIIKNMLMLMRQSQOOTVVY[[[[YWWVSUZYX[_`^^``^\\\]^]]\]]]`beijhhieegd^YTVVUROMMMPPRY_]]a]abbefddifefggd`_bbiV*#=H@=>A?859241)&()'(&$#$%%%'%""#&&&&()(),//.00./11-120/0+"'%$%'))(.241-+++,-,+-/,'&&" "%)-)(+)(*++-1112209AG@;Qgfbdec``dirhVKOUVZdXTWQGFJMOOLLOOMMHIOOKMSUPQTQQQNONPONOPNLOPPPRTUPNMNPQPOQPPRRQQSRQRTUVWY[XTUZ^][^bbbea`fdccceiklnx{x:!!""*4.%"#%1AMMJJNMIIIIHHHIJKHGGHJJKKLIHIIIHIIIJKJJJJMMKJKJHIJJJKJKKLLLLMMMJIIJLNLIJMKLMMLIINOLLMLKJIIHLOH?:9779=AAAAE?;VfPIVXTSTTUTPSQRUUQPRSUVSRTUUSPRWWVVUWWTMA>_!)-22255679;=>EDCA@???DDEEEDCCDEGFDBABDDDCCBBB@@@?@@@?=@BBBBA@ABCCABDCCDFFFEFGKLLJGGJLLJIIIHGFHDGHEEGEEGFGJFDGMJHGIIHGTddebTPM>:?=<>>=>>96=3$ANRQVdplirwqde[MJIGGJKHJRQNSTTRONPTVWX[\ZXXVWSRV\\\_][[]`_^\]]\[YYZ[]`bdhhiklhhie_YRVVURNKIIORV[_][]]bccddcchfeegheb`a`h_34?8421,%&,)&" #$"!!$'*+#! $&'&#$$$&*,++,,,,-//--)(.1.*-*'(,/0/-.,)(('#!!!!"!"$%((*,.--.31.,,**--*# %-0.8AG??BAFNOXZQKKHMRPIGMQPOJJMMPTSNLOSRRTRQPPONPQOOOONNPQQOQRQOPQRPOPQRQRSQQPOQTVWZ[YWY\[YZ^`ac``dddddefijny}B !"!&-0,'%&&.::56:@CB@@G?;?>>>=86<20MUVWY^ceforlfg\NFFHKHMMMPPORWSPQOMOUUUX[XXXVXVTWZ[Z]^^^^^^]^__`_][ZZ^bdefffhjgghfc^XRRQOMLLMSXZ\^]ZZ]bdcbcdeeeddegea_`_ie;*0+('($!  &!"$')+,,*'##%'&%$$##&*,**+,-,+-/./,*,//1210/-+)'#&%" !"&'&((),/0/0,,-///,+-3/))++-1&(2316AG?@Hh!'!386789;<>>>=CBBAAAAAGGFFFFFFHEBDHJHECCCBBABABBBAA@AAA@@?@@AACBAABBBCCCDEGHHHHIJKLLIHIJIGHJIFEIHFJHDFHGGEEHLKDGHHKOLDO`aefULJ@<@>;=<;><76=09RPORXY^`cjnj``YNGFGGIMONNQRQTTTQONPPQPUXYZ\YUVYYWV[]``_\[[]_[]^^][ZY[_cehgfhecefec^WVSNIGINQW[\\_^\Z\aeecdeedffdegfb]_aihB"$$#$%%""#!"$%%'*+'),/00/.3.(%&&%#%%%$'+,+&(,..,./,..--)(+$$$$" "!!"!)+,-0465224675311/,+,--,&//(),.10,'0>>QcdbbbaabfhpkI2<:2=ABEKNJIPNNOOLIMSPRPNOPQSORQPQPPTPSOKNONQQNNQRONOSSQNNPQPPRRPPQQORRTUVVWYZVW[\^_^^bdeeeegefgfggikrrzS!#!" !!%2:1)-0.&!"%.:CC@AGIGHKIIHJJJJIKLLKKJHJJIFGHIGKJJIIIIIIKLIHJJIKJIIJJIHKKKJIIKMLKLLJHLRKJLMKIJJKLKJKKIILIKLE=::89<@BBBCA;=ZfQJUSQRSSTUSSUVSRTVVTVVSQRTTUSTWYVUVWXSTXVLB?LYk~"&$7;9;==>@@AAADDEEDDBAGEB?<;:9989>EHGEDDCCBBBBECBCB@?@===?@@@@BBAABABDBCDFGHHIIJLKJIIIKLKJHHGFILHDFFDGFCEIGGHHFFGJLLIGRbaegVJI?;?>;>=>Qada`__`begvpL4<;-/716FNJJONLMQOJKQKOOMPPMLQRSQPPQQPSOKNNMPROMOQOOQORTTRQPPRPOQQPOPSQQTWWVUZYTU[ZVZ_`cc_`baegijhhikqox\*$##" (0--471*+--)$%'-6?A??CEGKLJHHIJIIHIJIIJJJIHIKJIHJHHHHHIJJLIIKKKLKJJIJJKKKIJKKKJKKGJLMMMKJJMMLNNLKLKJLMIGLMJJIB<;<58[ePKWTRRSQRRPRQQRRSTUUTSRRSTTWURSUSSWTURVWRQOL>;ETi#&&:><=@@@@ABCC?ACDDA><0-*&" #)1:CHEDDDDCCCC@@ACBAA@??@A?>=@BBAA?AGCDFGGFHIJIIKNNKHMLLLJFFIHEECADHFKGFFFFHKKJIIIHHHRb^ah\RN<9>=>95<2'HVNNQSSW[^fhc`YQJJLNLLQQMMPRRQTVTRPPQQRRRTTUVZ[ZY]^[Xa_]YY[]][[\[[\^__aabedbcbchjif^TMMLKKMPSTWY[]]]`ccffegiffkkfehgb_Z_dgQ&(&),.00111.+*((+02478778:71+(*+)&&''%%'))&('$##!"!$&&(./11100025992+,33454320/45311320.//.01.(+,.,*++)*61(,,&%2BF=@Q`c_^]]_adennR9@>+ )*)3DJJMOLMPOLLOOQNKNSQKOQRPOPONRQPNMOPONNMNOQRRROLMNOOORONPRQPPUTSSVYYVX[XW\\[a__cb]afcadhijjmolmy}f0$$##  ""09:2/133/)00(!!&/9??=?GJHIIHHHJKJIIHHHGGHJGHIJIGJMKJJIIIJJKGHKKKLMJJKKKKKLKKJJJKLMJMLIHKMLOMJLOMJKKKMNLIJMJHJLE=::89<@@@ACC;=YbMJUWRPRVXVQTTSSSSSSRRUVTQTXTSRTWUTWWTVYTRUVRIED?@LXks~#''<@=?A@??@ABCEED@90'!!0ABB@@B@@BBA??AA??BBBFFEDFHIGFJKLNNMJHIKLJGGHIEFGFFGGEEIHEGGDEGKKHGJJGTc[[e]SL?;?>;=<<<@;4;50KTLKLPQUY]eg`[VOJGIJHMQMJPRQTUVWVUVUSRUTSUUUXRZYW[ZWY\\[XY\]]]]]\[[\]]^]_dfefeehgdaZOMLKJKMQTW[^`a^_ccchjiihbejkecfe`aY\afW* +.+)+0541241*)-0/.157778:5/*)*+($"$$! "$$"%# "''"(++,2789>@A<635940154//475453002133026620100241,)+.--44*3D=,/4,%1BF=@Q_c^][\^acdniQAC9#&$,IOGNONLMNNNNPOPQNNOLNSPLPPOQTPRQLOSNLPPNNPPMONOQPNNPSRONQSPLRTSPQVXVXXVTW[\\_fc^abaffghgfgkoinzzm3%%#$!-1' )6<8,-41-15./*$$(.68?CEGGHMIHHIKJHFGGJLKIHIGIJIIIHFKJIIHHHIJHJKIIKLIJJJHHHIKJJKKLMMOLIJMMLKKKKKJLNLLLMKHKKFJHJKE=;;79>BCAABD=?ZcNJUTRSTSSTRQUVSQSTSUVUSRTUVUTRUYWVYVTYZWTRWQTSKDBCACDHLOW`d$'#,=>D<0%+?BEFEDDD?ACCA???BAA@??@AC@=>???ADEDDHKJHHHKMKLNLJKJHHHHFDFIGEEGFFEFHIHHIGGHJLKIHP]XWe`NLA=>?;:<=:=86:2CEFHFFIKJIHHFFGJIHGIJIFFHHHJIGHIIIJIHJMJJJJIHHHHKKLNMJKJJKKKKKJJKKJJLLJMJLNMLLLJKLLKJIHKJJLE<<:88AECA?@F=?ZbMLXUSQQRSRRTTTTTTSRSVTSTUTUUSSUVTTVRUWVVVUTUSQRTRLFEEEDCCBB"$".>BB?ACDCBBC<@DC@?AEA>=?@?@CC@??@ABCBGFEHIHKLKJJLLJIIIJKJIHGDFHGDEGFDGIFDEHIGIJIHIGDM\VQ`aQJA=@CA>=<;=75;3 #@RNMRRLOSWZ[[[TQMJHHIIMNOOPQTVXURTWZYYUUTRPOTYUVXYZZXTY\^]\\[Zaa`_`ba_]cedddddgkgbdbVLJMNMMORS_b_]afd`befedehjkgeedccd_\\\ff?'00+,36323/*((*))-,)&$"!  ####"$" !&-/.0343355531147:98<>EC8146..16863203446521544332/,/230/0/,,++8AADD@9:-"'3AE?=Ma^[YY[ZY\broG()1-!'HSIHKLOPMJKPQONOOMLMROOOOPNKPTUTROMPNJJOSQMJOMMOONORPQONNOOOQRQOOTWXWYXVXZZ[\`cdbbdfbefeegjlmqx{7'+"!%"/7.$#)485,*./04551(#$',3@EFFHGFIIIHHJKIEFIKIGHIJHHGFGHIKJHFHJJIJNLKJJJJIJKJIKKJLLKKJJJJJIIIJJKKKLLKMNLJLOLKMMKIHIIILG?<636@CBDCBA:@^cKIVSOPRORURSTTUTTSSRRTTSUUSRUWWVUSRTWURU[ZTXXWWUTTTSRPNKIGF #3A@@?@<;@FA3'#2AIBCEECBBC@@@??@CDE@>@@>>@ABBCDEECEFEEGHGFJMMLLJIMKJJKJGEFEFHGEEGFEGGDEFGFDIJGGJIEK]WO`gVHC<<>===;<=64<5 +HRNNRMNOQTVWWWRPNKIGGFHJMNOOPQPTWVTSVWVVVWVUVWUUTUWYXUTX\]^_^\]_`___aa]_`agihfjkhfe^SNJLMNPUY[c`_^adc^aefddfjjia^de`]aa]__gkI%..,,00/1/+%""!!#""!#$&'''('%&'&####(.0/06643.-353248<=<7>HG2$2BE?=L_]ZXXYYW[`jpI"')(%DOLMKLOPNJKNMOQQPNPRSRQONLKLMPQQSQOOKNOLKNPOKMPNMQSMOTSQSSPPORTQRUWUXYURX^_^___acdedeghgfilmoqw|{9 #$+& /5-%$(384-.52-.0:3'"$%)3DGHGIGFIJIIJIGHJHGGGJMLIJIIIGHHHJIIJJIJKLJGGIJJIJKJIJHGGKJIIIJKLLIHKMLLLKMKHJMMNMJHKLIHILLKJF@?838ABADB@@8?^eLKZRQUXTSTPQQRRRRRQTSTSQSUSRVYVUVURUVXVSRUXTVXVTTVYUUUUUUTT&9A<<=B??@9*0?DBCEFFDCDEAAA@>?AC@>;:<@B@ABA??BDCD@CFEILFIJJKMLIJJIHIHGGHFFGGDEFFGEDGHFEEFHHGILKFR`TGXgZLC;9;<<<:==44<6 3NSMOOKOPQRSTUUOPNKGEEGJLOQQPONSSVYVRSXVUVWWWUUUUTTVXYXYZ[Z[]__[^`_]]^``aaceefkfdgh_VRNIILPTX\`caaa]acaaddcdiife^]dd][__^cbclS#"&'%%&%$)(%!"#&&&'*.2589;73.'$&'#"%&&(.1003253&!,5335:>??@=95443/20,+*)''+,,/4434430--/001//0/,,-5DFA?:9?D4!1BE>>L\[XWVXWVY_mn@!*&AONNLLNQNKJMPOOPQQPOQSRPPMMRPPNOOONNOLIIJLMOQNPPLLOMMPQPRRPPPSUUUWVVVYWVZ][Z]_ababdfeffegjkkmpv|z7"++(.5/'#$1;3,085/04<8,$#"(7GJHHIHFILGEIJGGJIIHHIKIGHHJLIIIGIIJJIIJKLJIHIJJILLKJJJKKKJJIIIJJIKNNKIKPOJKNLIJKNMKLKIHKIJIIB<<969BCAB@>A9>]dLGQORUVUROORRRSSSSSQTTTVVTTTXXSPUWVTTRQTWVRQRTVUUTTWWWWWVVV)<@;>@AD>."+02369;>?@AEGBBBBCBA@?@?>@B@=ABA??ABBBBGHDGLIKIKONLJHJKJIHIIHGFFFDDFFFFHHGEEGKIFGJKHDR\PBSbXJB<<@A?=9==44<6 9OOLKNOQQQRRSSSLLLIGFHIHJLOSVWXVSSTUSUWVXYXVUWYTWXXXZ[[VZ]^``^[_^^_`_^]dccdfdejcegcYTQJHGLTXWWZbbfeabfdacb`chifa_aca]\`__cabl[+$''&))*.1,(&)-01-28>=?B?74552/////.,)*++,//.0310/.-.02..20*+13$0CE>>KYYWUUVVTX]ll=!)DRNHLLNPPKJKONNMNNNLRURNPRPPPQQOMMPRNMMMMMORPKNQLLPOOMMNMNQQQQSUVVX[UXZXYXY\\^aabbdfceeegkmmimw~x3#(,-4.'#$1;8..30,18>>2'&$*:IKIGJIGIGEFHJJIHHJJHGGHGIHIJGGIHIGFGIJHFIIJJJJIIJJJJJKKKJJJJJJJIJKLJJKKKLIJNMJJKMNLJKIIJIHIKD;:846?CBB@ABBB:1##'%"$((+.259>BECABCC@==<=@A@><<9<=64<5 !=NONLJMPQRSSSRRMLJIGGGGIIKNQSTSSWXURSUVTVWVUUUUVYZYYZ\][[[[\`aa]]^`_]]^`baafihfggb^[SKIJINW[ZZ\acedbefe`ccaaddb`aba_]_aaa_^an`3)/0/49;=?8.((,02.27::;;=950*&'(' $$"$+/./10044484347=?<654/*010/.-*'*,)'),-.2000/,-/2.,/0..0140%'15<;:-0DE=>JWWUTSUTSV\fl?! (BROIMLNQPLIJLOPONORSRTRQONMNONOOLMOPNOMJKOOLJKOPMOQORNOQNOQQQPQUUTUYZYUTXZ[^]]^adedcdghggilnglv~w1#" '.4,$#'278/.2/,.2?C6))(,:JKHGJJHIFFGIHGHJGGFFHJIGHHHGEEGGHHGGIJIFFHJKKJJJIIKLLKKIIIJKKKJJKMMKJKKJKNMJKKJIKLIHKKHHMJHKF><:34>CCBAC>:>X^LMXTQSTPRUSSSRRRSSSQRTTSTTQTVXWUSQQXUTUUSSUWXWURRUXSSTTUVVV':A@>5%!#%(,15;>AA@>??@>>>?@ABAABBAAACCGEDIJIJKGHKLKJIJKKKKKIHIEEEDCEEGFDCFIHEEFFEFHIGOYOBPaZJ@;:;98;;;=75;3 !?ONPNIIMOQSSRPOLLKJIGFEHHJNQSSRSRSVUQPRWWWVWXWUWYZYXZ\]^]\[]`a`Z\_a^[]`]bcabdefgaZYWMFIMOSX]``^bfd^`dgfbbb```_^ba`_^^`abd`]ald?)12.5>?>>7.(),0225:::9:;84/*$"$% &(%&,/-453113558657<@AA?>98;6.+,./.1330')%!'/22210.,,/1,-//,*.3.0--48@=2/DE=?ITUTRRSSRUZkg;+&# $?PNOMLNQQLIILLLJLOQQOMQTOJMRQNNNLLMJOOPPLHKQONNOMJLPOOPQQQQPRRSUUSSU\ZUTY[WUY]bdcbcedggedfhjhlu|w2"$!-5-#"(262,.4310.;F<+)).;JJGFJJIJKGEGIHGHHHHGHIIGEGHGGGGIHJJIIJJHJJKKLLKJIHHHHJLLKKKJJJJJMLJKMMLJJKKLNLJLMMKJMKGHMJGGB<<946?CBB@A@>AA>@@ACCABE@DFEEFHJQPNNOKJOKJIIIGGGIEDEDCEEDCDFGFEGFKKGDGHGJVM?L_YI?<>A=:;;:=86:2$BNIMOJJKMPSSROMGILLJHGGIHHJNQSTRSTUVVUTSWYXVVVVVXYYZ[ZY]\[[]acc]]^bca`aa_]_bbbfd_]YQNMJMSXZ^b^W]hieghebe`\\^`aae_\]^__aahc__hfI+43-3==8;6/+,.//-27:99:<;4.)""%!#!%/1-2025448996459=@@>=;<<406.133330*(*&"(00+231-+,./-)*--+-35/-"&3@G5+"/EE[bMJURQPPRSRQSSRRQQRRQTSRTUTTRRRSTUUUTTUWVSRRXURTWYVSUUUUVVWW-@?-(:C@=;:975/,'" """%',4;=?C>>>>>?@AB??BBBBBDEFFHIIJKLLLMLLLOMMKGHJJEFFEDEFHFECCDEGGHDFHEHIAMXK=L]YJ?;9;=;:9=?;088)FKJMKKIJMPRRPLHGHIJJIGFIIHHJNTXUVVTVYWRUUTUWUSXUYWX]\X[a^]]^ab_bb`aec````_`dd`]a]ZXSMKLKPW\]]^abegfffeba`^\\^adb`^]^`bab^^ccioN!"/3/2;=;51+)-.---0246:<<=7/'"""%!,1+.25435788315;>=;=9:91/465523411//*%#%*,-/10--..*.-.-)&,500-!$269B<%0EF<;FSUNSTPPTWVgjG((" (AQLLOKHHKMMKKLOQPOPSRPNNNNNONONLKKLLOOOPRRPNKOQPPNMPONOPRTTSTSTVWVTTZYUSVVUW]]]]`efegeeeffjnonux~A# #,4*&4850142...2@A4*&.@KHIIEDGJFHIIGEFFFGHIHHIJHHHIJHGIIEFJJKJFIJIGJMLHIKKJLJHIKKKJIHJNJLJIKJIKJKLLKKLMLJJLKHGHCHKJA::799?BCB@BC8=_cJJSSSSRPPQSSRTTPRTOTTSTTTTSTUVWVUSQUUUUTSSURRSUTSSUTWWTSVWV/:,%5>?????@@A>=83/,)%"%%%+4=>@@@A=@BA?ACCDHIGGKJGIIJLLLLKMNKHHFEGEGHFEFFECEEFEEEFEFEEGIHGNSKELZZJ@=;;<<<;9>;398 -EKMOIIKGNQQPQNIKKKJIGFEIGGIJLQWVTTWYWUVTYYUTUVVZUUZ\[\`^]^_\VYb^^_`]^``\]^_a`^`_[XWRLJLPRV]aca_^fifcdb^]_``^]_ac`aege`][`ccagoV' -313862.)'+.+),,,,.38;;:3,&!#$!" *-',12//34230/38:8775566325751123542+%$&'(*-//-,.-+(+-)%+48/3,#4F8'95"0EF<;FSTQTUQPSUTejB)BRMMOLIJMNMKNMMOPQRRMRSPNPNKKLMMMMMMOPPPPQQQMOOOPONOPQQQRTTSRTVVTSTWVZXTUXXX[\]`bcddfdeikkhfllsxH#!"+1'+784002111.3<<5-*1?KHHHGHHEIIIIJIHGGHJJIIIJHJJHIJIIMIFFFGJKIJLMKJIJIHJKJJKJJJJJHHJLLKJJJLKIJIIKKKKLLMKIILLILKIJC9:;6;A@@C@?@7<]`IJQQPPQRSSTPQSSPRSPQQQQRTUWTPSUQTWUVTRRSUTSSUVTTUUUUUVVVUSS" -8;>>>=<<>@@AA@?=952-(%%&-8?BBCCABDDBCDGHJJKJJKLLLKJJMLIJKHFIGFFGFCCEDEGFDCEFDGECGGEGKLHDHW\M>>==>?=:9;92840FLLMIKMLLORRMJIKKKJHGHHHFFGHHLRVSSVWSRTVWYYUSVX\YWWWY[\Z]]ZZ^]WWZ__^`cbeb_`ca^^`][XTOMOPSX\_aa`geege`[Z]\\\]_``aeggffb^_abeebi]4).*+.-)$'''*,*(-*()09=>;3-)&'&"" !)*''+,)*-.,-)(+1578469:8448872245861*%%&%&(-/.,,./--('))*+($*07C>*#*,%0EG=;ERSQSSPNOQQ^d=$?OKJNLKLOOMJMNOOOQRSSONQRONPNNNNMKKLLMNOPOPPOMLKNNMMPPQRRRSTSRRSTSTVRWWTTWXX]\_bdcdfddgjkiijiiqwT #!!+4,!!*693/./144/35860,4@JIHHHKIBGHIIHHHIGHIIIHGGGIHGHIHHIHHIJHGHJHHJKIIJJIJJIJLKJIIIHGIKLJKKJMMIJJIIKKKJNMKIKLKHGGHJC:;;79@CEDAA@9?]aKMSPOOQSTSRSTTRPPRSPRTUUTTTUTTSSSTTTSRSUWUQUWWVVVVUTVUTUXWS#*/168:<==>>>@AABB@=<;61.,-08ACCACDCDHHHHIKKKLLMMMKKKIGIHBGDCDCAADEFFFDDEFCFGHHHGHLOICGW[M>>=<=>>;==84831ELNOLIIJMNOPPLIIJKJHGHHHIHGEHJLQSTVVWWWVVYXUWZX[]ZWYZZ[ZY^_[]_W\a`_aeddhfddc`^a]^\XSPOORVZ]`cdd_bca\ZZ[\aed`]^a_egcadebbda``akd9(.,,-)$&&&(*)(*1.*+19>?82*('" $&%!!#'('''(+*)(*,,,-/4673498//4543/147:80+'&%##&,-.,,--,+)(-.)(..6DGFAC@9>]_JLSQQQRRRQOPRRPOOQTRRRRQRRRUVUTVVUWUTSSTUUVSSTVVTSTWVUVVUUT"&&'*,/4;?A?>==<;>??>>;999:;?BC@;>?@@B?@DCEEDFHJILKKKLLMNMJJKIHFCFGFCCFGEDEFFGEEDCEGGGEEFIKHCFTXG<==<<>=::;7383%3?GMRNKMJLOONMLKHIJIHGGGFHHEEFHHMQTTVYZXVVYZWXZYZ[Z[\ZXZZTX`_^_^adb_cfdbid__b`\\\_^VRQQOTUX\bfd_^^\\]]\[\`ca][^baaaacffdccdb_^ih>'-+,,)&&''())),---.28;<95+)+##38*%('%&&&&(,00.-),,)+0547784036232-,1485,+*'%#$&*,.-,+*))+'&,,1?FILHEG>$"16/EG=;EPQMMNNNNPS_^:!& %BTOMMMNOPOMLIMPONOPQQSQKJNQQOMLLLKMPMJJKMMNORPOMMMNNLLNOPOQSPPQTUTTTVUVWVVWX\^``abeefccfjklmjkqwi-#! )2,#%.9:5-,14553328;3,6CNIGHGHIHGFFGIJJIHHIIIHGFIHGGIGGIKKHIKIFFEHLLJHJMKKJHIIIJJJJKJIIJIKKIIIGGIKLKJJKLIKLKJJJJFKMJ@9;917@AAC@?B:@^aJLUQRSSQPOOOQSSSQQSUSPPQRRRRPQQQSVUSRTWWTSUUSSUVUUVRRUWWVVW#&%$%')*,17;==?>;99<>==;;?@<:>=:;?A??A?CA?AB@ADDDEGIKMLLKJJKKKLKIKMHEHCDDCCFECFEEFFFEEGJHGGECHKGFFDSZJ:;====;99<846745?GIIMMIJKLORMHHMGHHIIHGFFGHIHGHJLMOSUWWXXUX][VVYXW[]YZ[XU[\Z[Z\`^_ab`cc^`ca^[ZZ]\a`XRRRPPUZ]`b_ZXY\][XY]Y[_cedb``aaacfgegbacb^fiH&*)*+)(#()'),+*'*-/022130&$(!$9>,'-*'))&))).52)(++''/695862033375.-0341()*(&&'(,..,+*+,,*$%-08HMHLQJJF/"1;'/DG==AE=BabJMVPRSSQPPQPPQSRQQQTSRTVVSQRSRSSSRRURRUURRVUTSQRUVVRTUTSSTT$&&&'&)((*/5<=>>?A<9<@@??=@>BKNJLOJFHLONKKLMJIHHHIHFGHJIHGHHJILQVWXYTXZZ[[XUVX\[X^b]^_ZX]ZUW_^__]^`^^`_\]^^]^`_YSPPQRY__]]\YW[\Y[``\Z]`ba`ab`cdbbdeccc_[bdjlR##,./-(%'((*,*)+*,.-+)'%,)%&'#'58.*,,+--+0.''-+!&')('(/55652002464.,0462+*)*)'&',,,+)),.)).346CPHDEJGJL9&,7*!".DG>ED>C^^HKSRRRQQQRRSOPRRSUTPOOQTUTRPTSRVSPRTRRVWTRRVVTRSVWVYUTVVTVZ$()))'((),06;<==<<=<9<<<=<99>=>>:;>><@BAACA?EBBCEGHIJJKMNNMKJKHIKGDEFFECCDEEEDEFFDEFHFBFJFFHF@IH@BRYK;;PIFNNMLKLNOLKLOQPPPPPQQOMLMOONKJLNOOLKMMKLPQOOOMOQQPMLOPNNOMPRSRQTYWWYXSTY[\ZZ^```aeddhjjiiklqx<$ !.70$"+7;5579753.1:;7409MHJMKGHIGHGEFHJJIJIGGHIIIIHGGHGGJLHIJHHIHHKLJIKJHIKJJKJHHJIJLKIHHLHJMKHHIJMMKJLLJHKKIIKJHHJIHB==837>?ADCCD@E^]ILRUSQQQRRQTNPSRSTRQPPPQRRSTTSSSSSTUTTVVTSSUSSUVTSSWUSSSSSS#(*))*,-,-27>=::;<>>=88>?<;>====<=?@A?=?B@?DEEFEDGKLLMKJLKJKKIJKIEEHFFECBDEDCEEEDDFGFGFEEDEHHFFCBJZQ>9=;QIKKKMNNNMLJIIJMOOOQRTSQNLKMONNONMNNMMNOONNRRPPSRPPNQOMQQMKPPNNRSRSX]YTVWWZZYZ^___afeggfhlmnouv|G!"/<." )9:83000-+++/25639FLKIHGFEEFFHHFFHHIEGIHHHFIHGHJHGIIHHJJIIJKIIJJHHIKJHGJKKKKLKIHIKKIJMKGHKKJJJJJJKLGJMKIIIHGGIH@:8858?@AA@BD:C_]KOSPPPPPPQQQRQQRSSQQRSSQOPSURSVVSSVTRRUVTSUUSQTWTQTUUVVSQTX$*),.,)+/3::;<<=<<:<=;?C?>ACDB@CFDDFDDIKKNMIHKLILKHFFEDEFGFCBDFDEEDCDFHDFGGGGFGLFDDAETO9:@;8<@=:973388=CLKLOLJHKKLLJLLHHIGDDGHHJLIHKJGIKJLMIMTUSRRTX]^\[[ZYX]_\\ac`YVX[[\^`bb`_`\[_^[\`ced_ZWURONNSX[ZY[[`babcb_bbbcefffbadca``cccbebcrl>&.-*)*++*)(%$'++.21./0-*+03114-))/1..2."'1(#*,'%$!&4?<50...+&)*)'(./**)*-,'$%$$'**+/353139AEEIECCB?BHE8(/CE=;BLNKMKLMJKRW]B#=QIKKLMNNNMLMJHJNQPOSRQPOONMPNOPNOOLQOMOPONOPPPPOPONQROOPNLOOMPRQRUWWYYXXXXXX\^^]`dhfddfgkmlmosu~R""!3='$23-)%$$####'+1317EJIIHGGGHIGHIIIGDEEHLHDDGIHGHIHGIIHGHHHIJIIJJJJLOIIHHJKIHIIIKKKJJKJJJHIKKHJKKIHJLKLLKIIIJHFHHA;9737?ABB@AC7Aa]GKTQQPPOOOORQOOPRSSRRQQRSSQQUURSVVSSRPSYVQSRSUVUTTSXVSSUUTT$%(**-00.69;<<;<====<<;<<>=<=?@@?>=AB@BFDDBEHEHLJKKMMJJLLJLLGEFFCDCDEFDEGDFHHGFGIBEFGHGFGGDDCBFTM:6?@?;=>=741588;EKKKMJJJKJLKJJLJIIHGGHIJLJJIIIIGIJJIJKMOQRWYUX]]]YUW_c_Z]``\[^]YUZ_```ab`ba]\]abgjh_WTRPOORX]]\\Z]de`]__aabbddddcbbcda___dfba`bolA$/.)**,)))'&(+-+,24/,.0-*/4/&%,,---,--,"%.+"$+0*'$!&4??951,+)%(,0,(*02++-0/($$%"$)../1110,+-/.2124435:5.&/CF=;99=<9:?@?==>???A?>??BCABEDBDHJJHIHHJIGHJIGGEDCDDCBDEDCCEDCCDDDDBDDEEEDEGEBA@FUQ:7<;:;><=96445:CILJKLJHIKNMLLLKNLGGHEEIIHHJKHHIGGIGHLMKMQRTUTW]^ZZZ\^[[c____]YXYWZ[\_cc____^\[_deeaVNKLLKMQVZ][Z]`_^__`dc_acadeb`c^[_dbacedbffbepN"/-*+*+*++'*-,+*(,330212+*22)&+*+.,)+0-.((*&#*(%##"*8B8541*)+*-.01..2621.**,,*%#&/5533010-+,-,+)(+-++++*&/CE>ABB@AB:D_ZIOXQQQQQQPPSPPRTSQPRQQSRRRSQSUUSSTUUSTUSRSTSRUVSQSURVWUSSUW &(-11..4<>=::<9:;::;=>???>>>?@BA?>@@@BDEDCFGHIHJJJKJHJHEDDDABDBDEDCBCBDEEDCBCDCDDDFECDFABEDBNN88A>==>:<:7545ANPMLMOLIILNNLMKKNIDCDBDJKEIJJLIFGFFJKHIMNKOQSUUW[b[UX`_\]\^ab]VVYZ[\^`a_]a_^^`abbb]VPNMMLLOQSY^]Y[^__`__caacbbgjc`dc`dfc`aacbb^\dmW& +.--+('*,++,((*''/43144+&-0-..,++*((*+,'&)#!*%#$%'/;C;874/./.,.2311442/*(+/.*&(/5744521.))*-,*'&'*+,..-).BF>=BLMJKJJLIJP]Y@, 9RMILLLLMMNNNMLKLNPRRSSRQONMLOONOMNQQNMOQPONQPONPPOOOONLOQRQPOMOTUSSUXTOQSTWYZ]]]]affdefhjkjkmov{d)$#  !$&$(')**/=KKJIIHHIIJHGGGHIGEHHGIGFIIGIIGHJHIJJHIIIGJJIIKMKGJKJIKIHHFIKJJLKIKMKGIJJLIIJJKLMNKIIJKIHHFGJIB;9936>@AB@B@7B][HMTRRSSSRRQSOMNPQQRTPOQSSQPVSRRSRRTWSRTTUUSTTTTUTSUVSSVVSSW"+43./4:=<::<:==::>?=;=???>?@>BBBB@?DE@BHFEFFIGHJJKJGHFEDDEDDCDDCCEDCBEGFDCBCDFEFGFDCJFCECBOQ>7;;;;<;;:5459GTVNLMNMKMMKLNKJKKHGFGGFGIKKIIIJHHFGIJHIJKORPNPPT[]][[^_\ZZ^``_^[WXY^dc]]a`ba][^^Z\XSPNMLLOSSTYa`[Y]de`]__^```acccddcccbbecbb`bb^ah\-(.-***&',-*(('*)',22-31+),,*-/.*%%'%"#$&$""&%%&((.7<>?=81-,.,131-142+-021.,,./14543353.*(*)'))*)('()*-*-BF?=BKMJKJJLIIPZ[>#7ROIMLLLLMNNNKIIKNNNSTSRONNNPNNOPRSRNOPPNNNNOOKKNOMMOPQRQOPRQNOQQSROPWVRUURTWXZ^_`bgebcfhlljjkpwzh(&! """""!&'))(2CNHHHHHIJKGFGHGHHGHKHEEHGFHHIIGHIHJJIHGIIIIJKKJIGFKLJJJJIKJIJJIHILKIGIKKKNJIGIKMLKJIHHHHHJFGKJB;9858?@@B@CD8Cc^FHQPPQSSSRRRRSRPOPRQRQPQSRPURRUUSPPRRRRSRQQTVSPSUTSTTTUUUUV &-1159;::;=<<;:<>???>>===>>BBB@BBA@EFDFJHDHKHHKLMLGIHFCDEEDFDBBDEDBFCABEFDBEEEEHGEECBABBAML@:>;>?>78;8425GVTKKLMLJNMJKMKIIGFGHIJIHHMIHHGILJGIGFKJHJPMKNSTTUV[_^\]^\_`bc`ZXYXX[ceb`aba`^ZVVXUTQLJJJJORTTX^_[`_ab`adccaadebbf^]`dcabc\`c__b`anh:&0+%)/)&*.+()()*+-12/31/-+(&%*%#%&$$'"%'$!"#!! "#$)25=A?:60.330.,-00,',10+(+/4201587400.+***(%'*+****(-,!-BF?=BKLJKJJLHIPXX86RPIMLLKLMNOPNMMOPPPTSQONNNNKLPRPOPNPPQQNJLPPOMMNNNNPPQQPNPRSPQRQPPMOSROQSTUWWY]acccfdeghijgiiqx}zh("! ! ! $)&&4ELJIIHGGHHEFHIGGIJIGGHHGGEGIIGHHHHJHHHHHIJFGIIHGGHHJJJKIGGJGHKKHHKIIJKKJKKJKKKKKLMLLLIHHJKLIIG?:9:69@BBB@AB8Dc^FKVOOPPPOMLTSRQRSRPORTRPQSSQUWROPSSPRTTSSTUTUTRTWURSWXTSUUS !$(,05:<<;::>=;::==<=<<>??>=>@B?<@DB@ACBDIKGFIIJKIHIIFCBBBBBCAABBBDFACEEEDDEEEBBEFFGFA@CC?KOB9=>=9;<;=6325DOLEJNNKFJMLKLMJFGIFGGEFHGFHHHIIIKFEEDADIKMHJNPRTRXX[\Y\`a^bd`\[ZWX[^`bdca]]ZVUWVRWTOJLQQMPSWXZ]^^`cc`_]^c_^ac`beddaceccc`a^^_cebdmj>%2+#,.+').-(&*'*,**--%'(%$)+'&&'('&).&)'##%$%$$$$#)/2>>=<<@BA@CDADBABFHHGJIHIJGEHEEDDDEDCA@CFEDDCCDDEEEFFGHEBEFEFIECD@@JOC:9;;=:7;<7/18ISPJIMMKHGLJMPMJJJIIIIHHGFGKKJKLLLHDDEC@CJKMLKLNORSW[ZZ\_`\]``^__[X[]]_aa`X[ZVSTUUTTSQNMMNSVVW[^^]_aec__ba^^bdbbdcaababefb_acccccadjN+/---)%'-/,(++*)*'&('"#&&'))%%$%-31+'+,'&))&'''%*35/6>56A;311/-,*()+,0/)*263101111233.*+./.,)%',)##'&&%-@C=?@@C;F]UELROQPOMOUPOLNQRRSRRRQOPRTSQRUUQQSRTRRTSQQRQPQSSSTVSSUWURRU !"#*0:=99;:9::;;<<=<<===>>?>?@@>?AACDDEEFHIJGHJHFFGHECCCBBCDDCDECBCDCBCEFEDECFIFDFHHFCD@@IND99>=<:;<;9117GQPJKKJJGJLLNNLMMKHHHHHHHHKLMKJJJHIHHHFDDEGIHHMRQORUYZZZ]`]\^]ZY[\Y[]`cc_YVWVTRTWXVVURNMNOPUX[_`^]^bfebbcb`acccdb^_baaega[bcdd`]^bjoT#"-0,+,,)&*32,,*))*+))(&%&$%()+%#(.10/01-'&*)#$')*.9<73:59DA6-.,)&&(**-+*-24432235652021/../,(*'&'('()+'$.AC=>=<<=?=;=?>=@CEDCDFGGGIFHJHFFEFEDCBBA@DECCEDCFFECCDDCADEGGCBEDDBABAAJOA;<;;==;969549GOMLMIMRMJKJLOMKJHJIHGGFGGIIKKIJLJKKKJJHGFGHHJKJJMQRVYYX[`ZZ^`\WWX[\^_ba[TUUVWWVXY]XSQRROLSTTV]a`^`egfdbabbffccddcababe_\baaa`aabagmX)*.--+*)(,032-*)**)($&('$%')+-(%&(*.40+)*+(&&%%',5>=35=BFB4.-,+++,---&-1004521134541.,/0-+*& #&'''(*+*".AD==<<>@A>>B@?ACDCBCEFEDGGGIIGDFBDDCCDCAEDCCDCBDDDDDDDDDEIIFGHGHDC@B@?FKB9:=:::<:56239FQLJJLNLHKKKLKKNOLKJHHGHIIJHKMIKMKJGFIIGFIIIJMLHJRQQTWYZ\^[[_a^ZYWV[_`_]ZVVVW[ZXWYZZXSNKMPPTWZ\]^`cggdca_bcggefda`_ca`b_Z[_aababba`k^2&--.*+(',550-)),+*'$'+(%&('(/+''+00.-(',+%#'%%&-7BA97@LJ5$(1/++.-(&*+.475004:840.//.595-(*($#"%*/5>==>?>;=????>ACEDCCDFFGGGIFEFFDCCCABDCBCDCBCCEEEDDDCCBDEFGDBEFDABB?DID;<=:9:<<75026CNLLKIJJJIKLNNMNMHIIHFGGHHKILMJKNLKHHJJHILJJIIMONOQQSUY]^\\\_`__\WPW_bb_][[XXZ[Z[]`YROOOMJPUZ]_^]_`cdcca`cfc^_dc`_aca_aa`_e`_becaaboh:(-,*+,,*,2543-*,-+)'%(&$(($#)++)*,-+*)(%%&'''(*,1B8)%**.)'('%)/035400:FGE@:762,043,*,,'% #.106F6(0CE>=CLLJJKLKJMQUO7''((!/PTLKNLILMLLLMNOMLNRTTSQQQPNQOMMNLKLMNOPPPOOLNQONNONNOONOPPPSRQSURPSVXVSSSRQXXWX^``abccceghgllmt~k.#$#&(()+-)  &%(/56;BJHGGIIIHFFHHGFFHKIGHJKIGIGHIIFGHIGGIJJIIHHGGKJHGKKJIKIGJHGHIIHILJJIHJIHHGHKLKHHHIKKJJJHHIGJH@:8439A@?ABDB8F_UFOSOSQQRNQTQORTQNOQRQQQRRSSRTTSVSPPRSSSSTTRSRRRRQSUUUTRSVUR"&'$'5=:9;;;::<>;=>?>==>==>@@BB@ACDEDEEFGFGHFFFEDDCCBBBCB@BDCCDCCBBCEFFDDDGGCBDDEEBDDBEJD??<9;<;975116JSPGGMMIGJLJKLKJLMIIHGFFFFIHJLKLOOJKKJIIIHJJHJMMJIPQRTY^_\]]^__`]WV[_abca^_^^_aaaba]WROOOOTUVZ_`]]^acdca`aa^]aec`___^`ba`aaaaca`bgfqmA$,,,-+()*-2794,*+*((''&(+,(%)&%&)*(&&%$$%''')*+-/9BE?2,(%**&..*&&+/1/1007FQVOMJGIIB9364/-.,((,/57-(0:.!0CE>=CLLHIJJIILOWR9)'($/RVNKNKHKNLKPLJORPNQTRQQQQQQQOLMONLMNOPPPOOOOOONLNPPQPNPQOPSRORWSRTTOSSSTTRRVWXZ^__`cefffhigjnnr~k.  ""&'*)'% "!$+0.2;FJIEFLKEHKKIHIIGGIHFHKIDIHGHIIGFJFEGHHIHJIEEGIIJIKJGHIIHKIIJJIIJJJIIKKIIMKIHHIIIKLJHJJHFFEHG@<:72;@?A@;AC9IbWGNPOQMORQROLNRRNOQOMQSQNORSPRRSTTRQRRQRTUTRTTRQSUTRTTUVUSST")'$+7<:;<<;;;<;<<=>>??=>@?=>@?AABDFFFEFEEEDDFE?BCBBDDCECCCCBBCDCBBDDCBDEFDAEHFFFBCEACHG<<>;::>:852-9^hPHQPLLHKLMPOKKLKJIJIIIIIIHIKKKMOMNNLLLIFIJKMNKJLNPRUY\]^b_^^]^^[]]^^beebddccdeeba`\UPNQTPTX\^\\__`cedaabc`aba`acee_\``\]bbba``bdekmK!*-.-*$ (24/681)**(+(&&)*)'#&')+)'')'%'**((+347:>EJI9&"$'**+(**)-44.0-0?R[XPPPLHJPQNIC:1-+(%.8;83)(57/&1DF?=CLLFGHGFHJLWQ9'!.QUKMNKIMNLLMKKNPMMQSQPQRPPQLLMNPONPNNOOOOOPPLMOOPPMNRPMNPONRQOPVUQSTTRPRSQPTWY[]^^`a`cghgjokomqn/#''##)-(!  !%)*0:BJLEEJJEGHGEGIHDFJJGGIIEHHHGHJIEHEGJJIIHIIJJJIJILIIKJIJKIIIJKJIIJJIILLIIDLNHFKLHLKIHJKIIGFIH@<:62;ABEB=EC6E_TFQUNPRQOORPPSSOMOOKPQQOOQRPTSSRQQRRSTSRSVTQRTUSSSSQVTRRSTUU!&*($+;::;<<<;:==<<=>??:<>>:DA4EbWFOSOOTPLQPMNQPPSSPNMQRPMOSVURRSRSSONSUQORSQSQQTVUSSRTUTSSUV !$$$%$'0<:;=>:9<=>>=>@?=????ACB?A@@CDCDEGB@DECBDDCCDEEDC@ACCDDCC@BCCCDDDDDDDDDEEEEBBA?DGE;;<:;<>;;65-;:629>=@A?DB7IbUDJOOMPQMNPMNPQPOPQPOPQOOPQQNORSSUVSTSRQRSTTRRTUTRSTTTTTTUUV! !"#"! #!#&''()6=?;:99<>>>>>>>=?ABA?@??BCBACEDBEDCCAABECCBBBBBCABBCBBAABA@BDDCBCEFDBBEHADB@@CHI@9:;:;<;<=2/1:MRLKKKLKLNKLMMMMLKHJJHHJJIMKJLLJKOLMPOJILLJJLMLJIJMMMNPUY]^_adb\WUYZ\^bhkkffdbcffdfa[WUSRSQSV[_bba]difba`^_^_bcabfcbcedc`_b`^`bda^bbkf9'.*&())((,133>A3)+*$&/1)&,20..014421220.,-.0/10-374,*+--*)(,(%+59AO_RJT^VMPQOOOKKJGNHEIIEEIECA@@?:32,*#-DC>=AMJIIIGFGJMUQ/+JPMMKNOKKNMJMMMPQPPPPQRRQPQPPQQOONLLLMNMNOPOPMJLOONKLNNLNQPRRQSSQRWWWXXWWWXYYXWZ^__`aacdghjmmkss2$"""!%*'#'%*24:DIHHHGGGFEFHHGGHHGHHGGHFEFFGHHFGJKHHHHJHFGGHHIIIIJJIGFJGFHJIIIHHIJIHFEHIJJJJKLJJJIHIHDGGIG>973/:@=@C@A>8J_TFMPQOMMPPONNOPRRQONORSQPRRQSQQQRTUSPRTUTSRSRRRRRQRTUUTSSSRR! !%&$$&&#%'$$'&1:<:;;:=@=;=??=;>>==?BCB@@??ADCA?FGB@CD@BBCA@@ABDB@??ACD@@BCCBCEABCDDCCBDEA@@BGHB;;:8::8:=3/37AKOPMMMJGIONLKKJKLGIJJJJIIKLMLJKLKLLMLJKMJIJLNMKJJLKKLOSWY_`ced_[YUZ]]aghddddcefecgd^VRRRRQSW[_aa`]^bffb``b^^bdabfg``db_``b`_]`cdbc`hjC&-+)**(%$*/654HaTFNRNROMQOMRROMNPOPQOQSRRRRQPRUSPQTTTSRRTTTSQQQSTSQQQSUTQPSU!$&()*))(%$&&.8;:<<:<==>?=;;=@><>@BBBCDCBAABCCACCDBAAADCAAABBBEDCCCCA@FDBCDEEEHE@AA@EHD<;:9<=;<<9426?KLLLMMKIILLLMLLKKHGHHGFGHIHJNKJKLNNLKOPMLJIJLNNLLKLKLMQUX_`aba_\[X[\[^efcbdefdddddc]UORUTSQT\_]]`]^afhfca]`a__bca`bde`_`a``_^_ceed`hoM #++')*)$#'+787?A1&()+65)$+5.-,+((**,*''(+-/12202@D:1/-/32/,)**'0Na\VTTTPOOPLGHIFJLIGIIHGGFF@EDFJFCLLFB6*!0@B?=BLKIIIGFGJMWS0(GOLJLONJJLNLMKLPRPOOPQSRPOPMNOOMMNKNKILOLMTOPQQMOQOQPNMMPPOPOPQQTUTSTVVWWXXZ\]]\\\]ccbcdgjlijhny|w3'-"!#'& #)+/5@IIHGGGEEGIIIIIFGJGFFFIKIGIIHIIIIHFGHIHHHGIKJHHIIHGHIJHKKHGHIHJGFGIJIIEFGHHIJLIGGIGGGGEGKI?9855:>?@@>D@3HbSEPSPSNMTRLOQONOOPPQOOPRQPPRPQSSQSTTUSQRTUTRUSQRSSSTSTTTSSTU!$$"((''++(')8=:;==<<<>>=:;==?A@??>=BABCBAACB@@CECAABBCCCBBACBAABCBBABB@CFFC@BEFEDCCGC?AA@GLF=<;:==;<98428@IPNLKLJHIMKJHHIKLJJKLKHHIKHKNKIJJOPONNLKMMKJLNONNMMMKKMU[\_bcc`\YYZ[]`dfeeeeeddfhg_ZVRQRQSPS[_[[^Y_efc```___accbbdb``cb_]`^_accccffluY#!*,''*(##')1327B;)'*-0/''26((*)(()))))),.12/0203<>3464221/10((*'4KY]_^SJIKHLOLEDILJIHFDEFGGFIFFKFBIIJNIB;0-6@B=:BLLIIIGFGJMXT1&FPLJLNLKJKNLMKKOQOMRPPPQPPQONPQONOOKKLMOKKPNNQOMNQPOQQNNPRSQOQQQRTRTUUUTTTTXZ]^\\]^`abcefhhikkq|}v4'* ! "!"%$ %--4@HHGHHEFHIGFGKJFGHFEGFFIHEGGFFFGHIIGGGHGEDGJIFGHIHHHHHHJJIIKIDHGGGJKJGIIIIIIIIIKJHIIIIJIKG=7863;A>AB?A>7K_QFOQRRPORQMMNOPOOOPPOPPNOOQRRRSQOQUUSSSSSSSTSRSUVUSRTTSTUUUU$'%#&'&(-59:=>=<=<;<>><;<>>??@?=?BCB@ABB@BA?@CCABBAAAABBBA@@BCCB@CC@@DDBEEDEEFFFDD@??BJOG=<=;<:8;:7117@KNNKKNLIJMMMMLKJJIIJLKHGHKKLMKLLJLJLOLLNONLKKKKKLKNOMJMSY[_bdca]YXWZ`deeghfeghhhhd`]YQQROPQTY\\\\`\]dfb^_b\[beb_`eecbb_^ba`aba``beikt`,),(()'#%)*+372:D5$(+**(+3/'(+.-.-++-.0//..)(*.3>?4362---,0.)'))*;RV^^VPNHAGMKEDIKKGBBHIDBEJGFHHHGFHHJD@A>?:?B<9AKLIIIGFGJMWT1%EQLLLJJMMKKKMMLNONMRPNOPPPQNMOQOMNNLQPKJMONONOONOPPLNNNOQQRQQQTURPTTUUUUTTUWWXZZ[\^_`abcefgills}}t4& )2(!%"!))5CIEEHIGGGGGFFFHFFFEEFFHIIHHHGHEFFGGGFFIHGGIIHEHJIGHLKIJJIIHHHGKKJIIKJHIHHIIJJIJLJHHHFHBCGF<7625;@?AA>B>;K]SEKSQRRQNMOPNPPNMOPQNRQMLRSRQSTSOPSSRSSSRSSSTRRSSSSTSTTTSSTU! ")168449<<;9:<>=<<==?><;>A@==@A??@BA@@BCB@?@@ABA@@BCCBBBCCA?CCA?BED@CBBDGGD@CDA?>AJQF::=<;::9>:206ALNMLMNLIHLLLKKKLLKIHGGGILJHHLOMMOQNNOMNNLOMKLLKJJILONLMQUY[^^^_]Z\YY^bceifdehjhgg`aaYOQVTOSVX[^^[]]_ba^]^_abaabba_bc``a`_baa_^^adfjgne5',)(+*'),)*5;45EB+&+0,'+1/))-00121-/2210/0,/8AGLC0-.)(02+((()-+$/GYRMTYPGJIEEFEDGIGBADC?BIJIHFFGFCDDE?<@>?=?C>9AHIIIIGFGJMUQ0%ERMLLIINMJKLMLKNOOOONPQRPONOPRROMNNKLOONNNMLNPPQONNQRPNQQONQQQRTRSXSTUVVVWWVWY[\\]`cccdegijfihmwx}s1&49/ ! &5FIEFHGDGIHFEFGGHHFGHGGHGHIHGHHHIGFHHIKIHHHGFGHKIGFGGHHHIJHGHJKKJIGGGHHKKJIHGFFJIHHGDEGGFHF>::78:?@B?=D?9J_SCKTQPOOOOPPNOPOOOPOOQQNNQSRQQRSSTSOQQSTTSRQQQRTTRRSUTSSSTSS !"$# #&(19995588:<9:<=<<=>=<====?@@A?@AA??ABBBBA@??>ACCA@BDECBBCCCAABAABEFDBCDEEDDCDB?A@?JTJ<9=<:9;49844;GKQKKMJIJFLLMMLKJIGHHIIIJJKKHIMNKLKNMKMNMNRNMOQPNLKLLLKLPTX[]]^`_]][\`bbbdeddfedfijd`YOOSSPSVY\__\\]__``^]_^^`bbceebbb`abaa`^]_cdbjngmj?'-,*,+'+.**1:=9AG8+,1,(045/021//234553/-./35;@CIA/,20+-+')((),,,29FGEINJIURBAHCDJDKGAEFBEDEG@>B@@=>D@;AFFIIIGFGJMRO.%ERNIMKJLKIMNNJIMQQQQPPPONPRPRTRNNNMLIKMKKLJMONKKJKPPSQNOPPROPSTRSVVTUVVVVVWTVZ]\Z\`abcefghhmnlpy{s/(162+  %5EIGIICCFHHGIIIDHGFHHFFGEGIGFGGGHFEHIHJEGJLJGGJGIIGFGHGFJKHILJEIIIIIGHIHJKJIIJKHHIHEGIGGFHG?;:65>>>>>>>????????@@@@@@@@BBBBBBBB>??@ABBC??@ABBCCAABCDDEEEA@@@BMXL=:=;=?:<74535CRMMLLKJIIMMLKKJIIJIGFFGIJKLLLLMMMNNNNNNNNNNNNNNNNNMLKLNQRVX[^___^YZ\^aceedfhjjhfdcdc\TOORQTW[\\[Z\^accb`^bbbbbbbbcccccccc__abdfggdiejpG#/0.+*+++*,)8A9FIOWHEA><=?@B@=;>BB>GGDBCBBDCHE;9@EC=;AB=@JKJHHHGGIMQS3%ESIONLKKLNOJMOOOOQTTQQSRNNPOOOOOOOOLMOPPOMLNNNOOPPPRQPOOPQRTSRQRTWXUUUUUUUUQRUX[^_`bbcdfghhmhjry|t0!+53)"$)'(CKGFEFEFFGGHGFEEFHIIHFEGGGGGGGGGFGFGFGFHHHHHHHHHIJJJJIHFHIKKIHFJJIIIIJJIIJJKLMMLKJHFECCECHG=9:60C>7K\MDOTNOOOOPPPNPQPNNOPPPPPPPPPQQQQQQQQQQQQQQQQRRRRRRRRRSTUUUTT !!489:;;:<::::::::<<<<<<<<>>>>>>>>????????AAAAAAAABBBBBBBB??@AABCCAABBCDDEAAABCDDDEB@@AEOYI;:A>;:5:96206CNNNMLLKJJNMMLKKJJJJIIIIJJLLMMMNNNOOOOOOOOOOOOOOOOONLLMOQRUWZ]_`__]^^_`abbcdfhhgdcffc\TQSVQSW[]^]]Z\_abb`_ccccccccaaaaaaaaaabcdffgjh_esV  -1.,+++)(0138;;@K?.,58CIA)(+12/.0,,,--/013CG>?MSM2)*2128:9525=====;:;>DHH><@B>BHGGHJJGFJPZS0$CQIMLJJIKLMJKLMMNPQSQQQPONOOOOOOOOOJLMNNMLJNNOOOPPPQQPPPPQQRQPOPRTVTTTTTTTTRSUX[\^^bbcefghimhhpv{p0 !'-133390$#@E>9P]IESSNNNOOOPPPOMNPPONOOOOOOOOQQPQPQQQRRRRRRRRSSRSRSRSPQRTUUUU!"0478:867::::::::<<<<<<<<>>>>>>>>@@@@@@@@@@@@@@@@AAAAAAAA???@AABBAABBCDDDBCCCDEEEBA?@AEOXQ>8;97<>8<920:EKLLLKKJJILLLKJJIIHHIIIIHHKKKKLLLMMMMMMMMMMMMMMMMMMLLKLMOPVWZ]_`a`]^_`cdefdeghhgedfeaZSQTWOQUX[\]\Z\^abcbadddddddd________eeffggggabafp[*3:8520,(&/316?:9FC/*-2BK?/,+./-/3421/.---3BE=BVa\B55=;7:>5/+/;BC@E9,%&+0388:;>@BCB>>AB=97=EE=88:=<778614<@??>?A=4OcNBOPOOOOPPPPQONOPQPNPPPPPPPPQQQQQQQQPPPPPPPPTTTTTTTTPPRTUUVV  " #"/469::7899999999<<<<<<<>>>>>>>AAAAAAAAAAAAAAAA@@@AAABBAABBCCCDCCCDDEEEC@<:=EQZJ>9<:9;<9;7./;IOLLLLKKJJLLLKKJJJHIKLLKIHKKKLLLMMMMMMMMMMMMMMMMMMMLKLLMNOWXZ\]_^_\\]_acdeffghhhffgea[TPQRRTVYZ\[\[\^`aa``aaaaaaaabcbcbcbchhhhghggbadhm]2 4CA>:60+(-/29?=:@G<.*6HG8,**+,*+-+++,,./0>C@=GWXL>55;704:;7348;:863-%"$''++*,.379>=?CDC@?>B@:8987438EKJDBDBBA=@FIDGJJECHNRK;(%#! "-AOPKKJJIJJKNMKMOQPNPSRPOQQNPPPPPPPPMMNOONMMLLLLLLLLKLNOONLKPONOOPQRQQQQQQQQWXYZ[[[[\^`dgjlnlijpv{t0#%.5=@>A6'-DGDHJFGIFGHGDEFIEGHJIIFEGGGGGGGGFFFFFFFFGGGGGGGGGGHHHHGGGHIJIIHGIHGGGGHIGHHHHIIJKJIHFDCCEEHG@;836<>>ABAB<5K_RIPROOOPOPPPNPQPONOQPPPPPPPPQQPQPQQQRRRRRRRRSSRSRSRSPPRSSTTS                 +                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}~}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}zzzzzzzz{{{{{{{{yyyyyyyyxwxwxwxwwwwwwwwwuuuuuuuuxxxxxxxxvvvvvvvvxwxwxwxwvvvvvvvv{}}zyyyyyyyyyyyyyyyyyyzz{z{z{zzz{z{z{zxxxxxxxxvuuuttssqqrstuuvttttttttyyyyyyyyxy|}}|yxvvvvvvvv~~yyzyzyyyzz{{{{zzyxyxyxyx}}}}}}}}{{{zyxxwvvvvvvvv|}~wwwwwwwwxxxxxxxxyyyyyyyyxxxxxxxxxxxxxxxxwwwwwwww||||||||{{{{{{{{xxxxxxxxwwwwwwwwy|~}zwuuxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwyyyyyyyy||{{zyyyrsstuvwwuuuuuuuuqqqqqqqqsvy{{yvswwwwwwww~~zzzzzzzz}}~~}}}}}}}}}}}}}}}}}}}|{zyxwwuuuuuuuu{|}~zvtuvvuuvwxxxwwvtuvwxxxxvvvvvvvvyyyyyyyyzzyyyyzzyz{||||{{{{{{{{{zz{|}}}}~}|{zzzzy|~~{xvvuwxxwvvwwxxxxxwwwxxxxxxwvvvvvwwwvwwvvvxyyxwvwxyz{{{zzyyy{{{{{{{{yxvvvutrux{|{xvuvwwvttuv|~}{zyyzzyxy}~~||{zywvuuvwwvuuvyz|~~~~~}}zxy{{{{xxyyyyxxxxwwwwvwwwwwwwwwwwwwwwwwvvvvvwxxwwxxxxxxxxxxxxxxwxy{|}~{{{zzz{{{}~~{zz{{|||zyyyxxxyxxxwwxxxxxxwwwwvvvvvvvvuuuvwwwwwvvuuwwwwwwwwyyyyyyyyzyxyz{zy{}~~}{{{yyxxwxxx{~}|{yzzzyxy}~~~~~~~}~{{{zyxvvtuvwvvvwyz|~~}{}|yy{~~}|{zxvuuuvwwwwwwwwwwwwwwwwuuuuvwxxvvwwwvvuwwwwwwwwwwwxyyzzyyxxxxxy{}}|zxyz{{{|}}||zz{{|{{zz{{{{{{zzzyxxvvuvwwvuuvwvvwvwvvvuuvvwwxxvvvvvvvvwvvvxxxw||}|{z{||zxx{{{z{~}}{zyzyxwx|~}}~~~{{zzyxwvtuvvwvwwyz|z{~~~{y{|zy{}~~}{wuttuvuuuuuuuuvvvvvvvvuuuuuvvwvwxyxwutvvvwvwwwxxwwwwxxzyxwwwww}~~}ywvwxxxy{||z{||}|}|||}}}}}}|||{{zyxwyyyywwwxyxutrrrsuvvvvwwwwwwwwwwwyxvwwwutxz{{yxxyzyxyz{{{{~}}|zzzzxvx|}}|~~}~~~~zzzzyxxwtuvwvwwwyz|}~~|y{|yxyyyyz{{{{{{||{ywutuwxvuvuvuvvssssssssuutssssswxz{{ywuwwwwxxxxxxxxxyz{zzyxxxxx}|xvvyxxxzzxwxyzzzzzzz{{{{{{z{{{{{zyxyzzzyxxyyxwvvvxytttttsssttttttttwvuuvvusvy||{ywvvwxwwwyz{~}}{zyzyxwx{~||}~}{{{{||}}~~~}zzzzyxxwvvvvwvvuyz|~zxwwwwwyxxwwwwwwxxvvuwxyxxxxxxxxwwwwwwwwxxvvtutuz{}}{yzzz{{|||yxwvvwxxwwwwwwxx{}~{xvvxxxyyyyxwxxyyyyyxxyyyyxxxyyzyyxwwxyyxwwxxxwwvuttwwvvuutttttttttttsstvwwvtw{|zxutuwwwuuwy{~}{zyyz{yxx|~}{}}|{|{|||||{z{|~~~}{{zzyxwvvvvvvwuuyz|{yxxwvvwxxwwwxyywwwwwwwwyyyyyyyyzzzzzzzzzzyxxxxx{|}}~}}|||||}}~~{ywvuuuvvvvvwwxx{|}|yvuuvvwxxwxxwwxyyyyywxxxxxxwvwxxywvuuvxxwwwwwwwxxxxxxxxxxxxx{{{{{{{{xwwxz{{{vx{{zxwvxxxwwvwx{~}zyxy{{{zy}}|}~|~~~~~~~}{{{|~~}{{{zyxvvvuuvwwvuyz|~~~xwvwwwxzyywvvvvvwwxxxwvuxxxxxxxxwwwwwwwwyyxxxxyyyxxxxyzzyyyzz{{{|{zyxy{|{zzyyyyy|xutuuvwvtsssuvvwwxxxwwwxxwwwwxxyywvutvwxwwwwxwuuuvwxvvvwwxxxxxxxxxxxxwvwxxwv}~}||}|zxxyyxw|~}yxxy{|{{z}}|~~}~~~}}||~~~}||{zywvuuttvwxwvyz|~}|~xnnx}}~}}}}zywuuuuvxwwwwvvvvvvvvvvvvvvvvvvvywuuvwxxwxxxxwwvxxxyyzzz|||{{{{{}zzz~}zxwwxy|{zyxwvvuuuuuuuuwwvvvuuuuuvvwwxxuuttuwz{vvvvvvvvyyxxwwwvvwwuuwvux{~~{xxxxxwwxz|~|~}{zxvvwxzxxyzz{||{{|}}~~~}}zzyyxxwwzzzzzzzz{|}~|{~xpqy|}~|zwussstttsssrrttstststvvvvvvvvvuuvwwwvvvwxxxxwxxxyyyyyxxyyyzzz~|z{|}}{{{}~~~~~~~~~zzzzzzzzzzzzzzzzuuvvwwwxponnoprsssssssssyxxxwwwwwxxwwyywx{~~{yxyvvuuvxyz}~}|zxvuuvwuvvwwxyywwxyyzz{yyyzzzzz||{{zzzz~~~~~~~~{|}}~~|}}~~~}wwzzyzz~~~~~~|{zxwwwwwvvvvuuuttttttttttttttttuwy{zyxwvvwwwwvvwwwvwvvvvwwxyz{{}}~}{z{||{zyyyyzxxyyyyzz{{{{{{{{wxxyyz{{zzz{{{||vvuuuuvwrrrrrrrrtttttsssrttstuvtx{~}{xxxxwvuvwxy}~~}{zxvutuussttuuvvuuvvwxxxyzzz{|||~~~}}}}|||}|xyzyxyxyyxxxyyyyyyyyyzz{{zzzyyyxxxxxxxxuuuuuuuuy|~~|{{||{{zywvvvuuttssuuvvwxxx{}~~|zyy}}|{zzyyzzzyxwwwxxxxxxxxuuvwxyzzxxxxxyyyzzyyxxxxyyyyyyyywwwwwwwwtvvuvxxwuxz{xutuwvttsuuv{~~}}zywvuuuvuuuvvwwwxyyzzzz{}}}~~~~~~~~~~~}}}}}}}}|}}{xxzxxxwwwwwwwxxxxxxxwwvzzzyyyxxzzzzzzzz{z{z{z{{|}~|{{{}|{xwwvvuuutttttttt{}~zwvwwxyzz{{zzzyyxxxwwwwwwwwvvwxyz{{wwwvvvvvyyyyxxwwyyyyyyyyyyyzzzzzxz{y{|}|x{~}{xxxzywvvvxxz}}}|xxwwwxxxyyzzzz{z||}|}}}}~~~}}}||}}}}~}~~|||{|{|||}~{vw{zyyxwwvvvvvwuuvwwwvvyyyxxxwwyyyyyyyy{{{{{{{{zzzyxxxx||}}}}||zzzzyyyyyyxxxxxx|~~zwvuvwxxyxwsttuvwxxttttttttuuvwwxxyxxwwwvvvxxyzzzyxwwwwwwwwxxxxxyyyxzzyy{{z{~~{z{}}{zz{|}{~~}||xyyyzz{{}}}}}}}}}}}~}~}~~~~}||||||}}~~~}~}~}~}}}}~wpr{}||{{{{{wxyzzzyyyyxxxwwwxxxwxxxxwwwwwwwwwwvwwxwwwwxyyyyxzzzz{{{{{{|||||}~~|zzz||}~~|{zvvwxyz{{ttttttttvvvvvvvvuuuttsssrsuwwwvvxxxxxxxxwxxxyyyyxyyxxyzxxz~}{xxxzyxwxxz{|~}||{{||||||~~~~~~~~}}}}}}}}~~~~~~}}}}~~}~~}ypr}~|zxvvvuuuttxxxxxxxxwwwwwwwwwvvwyzyxxxyyyxwwxxxyyzzzxxyyz{||~|zz}xy{}~~~}{{{{{{{{zzzzzzzz{{zzzzyyxxxwvvvupqtvwwvussssssssssssttuuvxxvvwwvy||yyyxwvvvwyz|~~}||~~~~~}||}}}}}}}}}}}}}|||{|||}}~~~~~~~~~~~~}~{}~~~wxyz{|~~~~~~~~~~~~}wwwwwwwwwwvusrqqsssssssswwwwwwwwwwxxxyyyyyyyxxwwyyyzzz{{}~~}}{zyzzzzzzzzyyyxxwww{{{{{{{{{|}|zyz{||||||||xy{}~~~~ttttttttrrrqqqpptrqrtutsvwyzzywvtuvvvwy{|}|{||}}~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}~}xyyyyyyyzzzzzzzzzzzzz{{{xxxxxxxxzzyxwwvvuuuuuuuuzzzzzzzzvwwwxxxxwwvvuuuuxxxyyyyz~~}}{{zzzzzzzz{{{zzzyyxxxxxxxxyz{zxwwyzzzzzzzzwxz||}}|{{{{{{{{{zyywvuuwvuwz|{zuvxyyxvuuvvutuvx|}|zz{{{||}||||||||~~~~~~~~~~~~~~~~~~~zzyxwvuuvvvvvvvvttuuvwxxwwwwwwwwzyzyyyyyyyyyyyyy{{{{{{{{zzz{{|||zzzyyyxx{{{{||||~}|{vuvuvuvuwwwvvuuuyxyxyxyyyz{zxwxyyyyyyyyyxyz{}|||zzzz{zzzwxyy{|}}}|{}{|~~|{{||zyyz{|~~}{{||}}}}~~~~~~~~~~~~~~~~{|}~~~~~{{zywwvuwwwwwwwwtuuuuvvwuuuuuuuuvvvwwxwxyyyyyyyyxyxyxyxx{{|||}}}}}}}|||{{||||}}}}~~~}}|{uututututtstssrrtttuttttuvwvtssuvvvvvvvvtuvxxxwwzzzzzzzzyyyzz{{{|zyyzzxw{|~~|{{||{yyz||~~~~~~~~~~~~~~~~~~~~~~~}~~~~~{{{{{zzzzzzzzzzzzzzyyxxxvvvvvvvvvvvvwwwwuuvuvuuuxxxwxwxxxxxyyyzzzzzyyxxxxxyyzyzz{||||{{zzzzyzyzzyyyxxwwwvvvvvvvvwxywvuvwttttttttopqrsrrqutututuuwwwwxxyyzxvwxxwuz{}~~}{zyzzyxyz||~~~~}}}|~}|{~}}||{{{||~{||}}}~~}}}}}}}}~~}}||{{{{{{{{{{z{zzzzzzyzyzyzyzzzz{z{zzyyyzz{{{yyyxxxwwyyyzz{{{z{{|{{zz{{{{{{{{}}||{{{{}}|}|}|}}~~|{|}yyyyyyyytuvwwvuuqqpqpqqqnooqrstussstxzyyxy{||{yxxxyxwxy{|~{{zzyzyy||{{zzyyz{}|||}}}~~}}}}}}}}~~~}}}}}~~~~~~~~~}||{~~~~~~~~}}}}}}}}|}}}~~~}}|||{{{||||}}}}z{||||{{{{{z{z{{|||{{zzz{z{z{z{{{|}|zyz{}}}}}}}}{|}}}|{{yyyyyyyyuutsrpooooorvxyxtuwxxwutvvvutsuv|~~}}}}}}}}zyzyzyzzzzzzzyyyxwwvuttsxy||||||}}}||||||||{{{|}}~~}}}}}}}}}|{{{{{{{{{{}}}}}}}}|||}}}~~~~~}}}||{{{||||}{||}}}||}}}}}}}}}}||{{{{{{{{{{{{{|}|zyz{{{{{{{{{zz{|{{yy||||||||}}||{{{{yyyz~yz|}}|zy{{{ywvwx|~~~}}|{zzyxxxxxxxxxyyyyyyyy~}{xtqomnnmmkkjjvx{~~}|{z|~~}}}}}}}}}}}}}}}}}}}|}}}}}}}}}}}}~~~~~~~~||||{{{{}}}}}}}}||||||||}}}}}}}}||||||||||}}}}|{{{{|||}}}}}}}}}}}}}}}}}}~~}|||}||{zyxx{{{{{{{{zzzzzzzz}}||||||~}}|||}}~~~}}~~~~~~~~~~~~|zzzzzyxxyz}~{{||}}~~~zpjgdgosqlghnu}~|{~~}|{zyyxxyz{zxu||xuv{~||||||||||||||||}}}}}}}}}}|}|}|}}}}}}}}}}}}}||||}}}~}~}}||||||||}}}}}}}}||||||||z{|}}~}}{{|||}}}}}}}}}}}}}}~}}}}~~~}|||~~}}||{{||||||||||||||||}}|{{{||{{zzzz{{|}}~~}}|||||||||~~~}||~~~~~~}{wtutllv||xpkmu}|zxwzzzzz{{{zzz{|{yw{~}xuw{{{{{{{{{||||||||}}}}}}}}|||{|{||||||||||~~~}}|||~~~~~~~~}|}|}|}}}|}|}|}|||||||||yz|}~~~~||||}}}}~}~}~}~~~~~~~~~~~~}}|||~~~~~~~~}}}}}}}}}}}}}}}}}}||{{||||{{{{||{||}}}||{{{{{{{{~~}~}{ury{tx~wpou}~|{z{|yzz{||}}|||}}}{yz|~yvw|~{{{{{{{{{{{{{{{{||{|{|||{{{{{{{{{{{|{|{{}}}}||{{}}|}|}}}}}}}|}}}||||||||||||||||z{|}}~}}|||}}}}~~~~~}~~~~~~~~~~~~~~~}}~~~~~~~~||||||||||||||||~}}||||}}}|||}~~{|}}~}}|{{{{{{{{}}||~~}}|||vlowy~zrpu}~|{}~~}~}~}~~}}~~}{y{~~yvx|{{{{{{{{{{{{{{{{zzzzzzzz{z{z{z{z{{{{{{{{|{{{{zzz{{{{{{{{||}|}|}|{{{{{{{{{{{{{{{{||}}~}||||||}}}}}}~}~}~}~~~}~}~~~~~~~~}}}|}}}}}}}}||||||||~~}}}}}}||{{||}~|}~~~~||||||||~|{{}}}}}}}}}xihs|}~}~|snu}|{{}~~~~~~~~~~~~~|yz|~ywx}{{{{{{{{||||||||zzzzzzzz{{z{z{{{{{{{{{{{{{zzzzyyzzzzzzzz||{|{|{|z{z{z{z{zzzzzzzz|}}~}}|{{{{{||||}}|}|}|}}}}}}}}}~~}~}}}}~~~}}||~~~~~~~~||||||||~}}|||}}||||}}~|}~}}}}}}}}~~}~~~~~~~~~~xlku~~|||uqu}}{zz{|}z{{|}~~~~~~~~~}{{|}yvx|||||||||||||||||{{{{{{{{{{{{{{{{|||{|{||{{{{zzzzzzzzzzzz{{{{{{{{zzzzzzzzyyyyyyyy{|}}~}}|zzzz{{{|||||||||||||||||{{{{{{{{|{{{{{{{||||||||{{{{{{{{||{{{{{{||||}~~{|}~}}}}}}}}~~~}}||{|}}vmpy}}||uru}yyz{|}~~{{||||}}~}}}~}||{|~}xvw{}}}}}}}}}}}}}}}}}}}}}}}}{{{{{{{{||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzzzzzxxxxxxxxzz|}~~~~yyzzz{{{{{{{{{{{{{{{{{{{yyyyyyyyxxxyyzzzzzzzzzzzyyyyyyyy{{zzyyzzyyyyz{||yz|}~~~~||||||||}~}~}~~}|zyxxy}~ulqz{{~|qmu}}}~~~}|~||{z}||}}}|}||~~|xuv{~|ywwz}}~~}}}}}}}|}}}}}}}}}}|{{{|}}}}}}}}}}~}|~}}}~~}zz|~}|||||||||x{~|zzzzzzzz}|zyxxyyzz{{{{{{{ywvvx{}zzzzzzzzzzzzzzzzyyyyyyyyzyzyzyzzuwxyyz{}yz{|}||{yyzz{{||}~}|~vorvzymmx~vmmu{||}}}}}|}}}}}~}}}~~}~~|{|~~yuvz~~~~~~~~|yvvy}~~}}~~|{xxxxxxxx{}~}{{|~{|||}}~~|}}}}}}}}}xz~}{||||||||}|{||}~~||{||}||zzzz{|zzzzzzzz}}|{yxww{{{{{{{{xxxxxxxxyyyyyxyyz{{||{zyyyyzz{{{|~~~}{yqkovx|xnmw~|slnu{|}}~~~~~}}}~~~~~~~}}~||{~~yuvz~~{xvvy}}zuuuuuuuuz{}|zy{|{{{{||}}~{yx{||{xxxxxxxxwy|~}{z{{{{{{{{~}{{{|}~~~~~~~~}|{z}}}}}}}}~}}||{{{zzzzzzzzyyyyyyyy}}|{zzyx}}~}}{zyzzzz{{|||~~~}}}~~~{vnlrtquvomv|wskiou{}}~~~~~~}~~}}||}|{|}}xuvz~ywtvy~{vvvvvvvvz|}}{z{}|||}}~~~|yvwxzzywwwwwwwwxy{||{zxwwwwwwwwzywvvwxyz|~|ywyzyzyzyytuvxyz{|{{{{{{{{zzzzzzzz~||}}|z~|zy{|{|||||{~~}|{{|}||}}vqryuprumhow~~wpjdeou{}}~~}}}~}}}}}}~~||{~}wuvz~~~~~~~~~~~xutty~}xxxxxxxx|~~}|}~~~}zz}~~|}}}}}}}}yyz{zzyxwwwwwwwwwvuuuvwx{}~|xvssssssssnnpprsttrrrrrrrrwwwvwwwwzxwy|~}{~~~~|zxwzzzzzyzyz~~}|{zz{|z{|~~vqtyxrstkcgn{|xrmeelu{||~~~}{{|}}}}~|{|}|vtvz}~~~~~~~~~~~~|{}vtrty~xxxxxxxx}}}~|yz{~}|~~~~~~~~{{zzzyyy{{{{{{{{{zyyyz|}~}ywwwvwvwwwxxvusrqqoooooooorrrrrrrrsqpty||zzzzzzxwvxxwwvvvvy~~~}|zzz{{zz{}~~uopqqostkflsx|}}yohku{|}}~~~}}~~|{{||{zz|}~~||{~{utwz|~~~~usrsy~~~~~~|~yyyyyyyy|}~|{}~~~~{xxz||z{{{{{{{{}|{zzyzz||||||||~~}}}}{||||||||~}zxutsyyyyyyyysssssssssposz~}zyz{{{zyyyyxxwwwvy~~~}|{zz{|z{||~wrqnlowwnlu{y}vmmu{}~~~~}}|}}{}~}{zyz{{{|{|}zttwz|~}tsqsx~~}||}}}}}}}}y}}xw{{{{{{{{{z|}|zz{}}}}~~||~~~~~~~~~}|{zzzzzzzzzzzz~~xxxxxxxxyxwvtsrrwwwwwwwwwwwwwwwwwsrv}||}~~~|||{{zzyx~~}|{{{{|{{||}~~|wvqpv~zomvz{}}xppu{~~}}||~}}~|{|||||{{~zttwz|}}~~}wursx~~~}|{{{|}|{zyyz{|zzzzzyyy}}xuvwwzzzz|~~zx|~~~~}}~~~}|z|~~{wx{~~|{{|wz~~yxwvuvxzy||wuvxwvwxyyxwvtuuuvvvwvutvz|}}}~}|}~}{yxy{}}|{{zzy{{|}~~}vpqz{smty~~}|||||}}~~~~~}}||||{{}}vtv{~~~~{|}}~~}wsopv~}|}}~||{zz{|}||||{{{{{~~{z|}}z{}~|z{~~}~~}||}~}|z{}~|yz{|}}|zyx{~}|zywxz{y||wuvxwwwxxxwwvvvvvwwwwvutvz}~~}~~}{}~~{yyy{}}||{zzz{{|}~~~~|wsu|}xqmty~~||{z||}}~~~~~||}}~|{{~}vtv{~~~~~}}~{|}}~}yvrrx}}||||}}{{{zzyyy{~~}~|{{~~|}|}}}~}}|~|zz}~}|}{z{}}zvz{~|yxxyzy||wuvxwxxwvvvvvwwwwwvvvvutvz}~}~~}{}~}{yyz{}}||{{zz{{|}~~}}{|{wsty}xxxvspnmuy~~~~}}||{}}}}~~~~~~}}}|{{}}vtv{~}|~~~~~~~~}{|}}~}~|yy|~~~~~~{{zzyxxx|}~~~~{y|~||{|~}|~|z{|~~~~|{{|{yx{}~zxwwvvy||wuvxwyxvvuvvwwwwvvuuuvutwz~~~}z|~~{zyz{~}}|{{zzz{{}}xnijmmnnmihikuy~}~}~}~~~||}}}}}~}~~~~}|||{{~~}vtv{~~|}{|}}~}}|}~~~}||{{z|~~~{y|~}{{}}~~~~~}|{}~}~~}{z{|}~~zwxwvuy||wuvxwyxvuuuwwxwwvvuuuvtuw{~~~|z|~}|zz{{~~}}|{{zzz{|~~xlddhmnnjgegivy~~~~}}}}}||||||||}}|{|{|||{{}~~}vtv{~}}{{}}}~}||~}|{zz|~||}|zz|}}}~~~~~}}}~~|~}{{}zxyzxvy||wuvxwxwvvuvwxyxxxwwvvuutw{~}|y{}}{zz{{~~}}|{{{zz{|}~xmghlmmmlkklnwz~~}{{zz||||||||{|{|||{{|{{~~~}vtv{~}~~~~~~~~}z|}~}~~|{zzy~~~||}|yy{{{|}~~}}~~~~}~~}}}}xwyzyvy||wuvxwwwvvwwxyxxxxwwwwutuw|}|y{}}|z{|{~~~~}||{{zz{|}~}}}ysomlmpppruvwvwz}~}}||{{z}}}}}}}}z{|}~|{z|{{}}vtv{~~}}}~~~~|{{}~}~}}~~}||}{}|y{}{yx|{zzz|}~~}|}{|~|zutwywty||wuvxwvvvwwxyywwwwvvvvutux|}{y{}}{{{|{~~~~}||{{zz{|}~|yvsqpqmikqwyy}{{}|xz}|||}}~~~~~~~~~~~|||}}}|||{{}}vtv{~~~~~~~~|z|}~}~||}~}}|zz~~~~zy}}yxzz}}{{}~||~~{ttwzywz|{vsuxxyxwvvwyzvvwwxwvvvutw{~{x}}}|yz}}}}~|||yz{|}~|yvqichfflu|~~~~}}z{~~}}||{{zz{||}~~~}}~~~~}}~|{|}~xwy}|{}~}~~~~~~~~~~||||~~||}~}~}~}}~zy|}zy|~}|||||}}~}|}}{utwyxvz|zvsvxxwvuuuvwxwwvvvvvwvutw{~{x}}}{y{}}}}~}{{|yzz|}~~~~~vkchfflu|~~~~}}z{~}~}}||{{}}}}}|||}}~~~~}}~|{|}~xvw{~|{}~}~~~~~~~~~~||||~~}|~~|}}~~zy{}{|~|z|}|{||}~~~}|}}~~}zvuwyxvz|{usuxxwwwwwwxxwwvuuvwwvutw{~~zy~~||{y{}}}|}~}|{z{yz{{|}~~~}}}}~{ofigglv|~}~~}}z{~}}~~~}|{~~}}|||{}}~~~~}}~|{|}~xvvy{}{}~}~~~~~~~~~~||||~~}}~}}}~}|y{{{|~|zz{}||}{{{||}||}~~~}{{xuuwyyxz|zvsvxxvwwyxxxwwvvvuvvwvutw{~}~yz~}{{zy{~}}{{|{{zzzyzz{{|}}|||||}~|smjhgmu}~}~~}}z{~|}~~~||~}}}|}}}}~~~~}}}~|{|~}{xvwy{}|}~}~~~~~~~~~~~|}}|~~~}}~}{z{{||z|}|{|}|zzzz{{|}~~~{yuttuxz{{z|{utuxxtuvwwwvuuuvvwvvuvutw{~}~y{~|zzzx|~}}{zzzzzzyzzzz{{||{{{{|}~{upkiimv|~}~~}}z{~|}~~~~}~~}}||}}~~~~~}}|~|{|}|~}{ywxy{|}|~~}~~~~~~~~~~}}}}}}}~~~~{{{{{{{~}{{{zzyyyz|}~~~|yxsstvxyz{z|zvsvxxuvvwwwvvuuvwwvuuvutw{~~~z|{xyyx|}}{zyxyzzyzzzzz{z{zzz{|}~|tmljinv}~}~~}}z{~}}}}}~~~}||||~~~~~}||~|{|~{{yxxxy{|}~|~~|~~~~~~~~~}}}}|}~~~~}~}~}|{{{|{z}~~}}|{zzyyyz|~~}{yxwtuwwwwwwz|{vsuxxvwwwxwxwuuvvvvvuvutw{~~{|zwxyx|}}{zxxyzzzzzzzzzzzzzz{|}|skmjjnw}~}~~}}z{~}||||}~|||}}~~~~~}||~|{|}zyxxyyz{||~|~~|~~~~~~~~~}}}}||~~~~}}}~|~z{{z{|}zzz{||{z}{{~}||zzyyz|~~}{yxwwwxyxvuttz|{vsuxxuuuvvvvvvvuutuvvvutw{~~}||zwxyx}}}|zxxy{{zzzzzyyyyzzz{|~|slmkjow}~}~~}}z{~}|{{{}~~}||}~~~~~}||~|{|~yxyyzz{{zz~|~~|~~~~~~~~~}~~}{}~{||z}}~~~~~zy||||}}~~~{x{~~~~|zxwvvwxxxxvutx{{ustvuxussuwxxwwvsstututtvz~~~~{zwxxwy~|}zxxz{{zyyyxyzz{yyyz{}~}xpkokhlt{~}|}}|yz}}||~~{~~~~~~~~~}||~~}{xxvyytty}~|}~}~~~|}|~~~{}}~~|}}~~~~{z|||||}}}~~yvy|}~~}{yxwvvwxxxwwutx{zustvuxxvvuuvwutttssttuttvz~}}{zwxxwy~~|}{yyz{{zyyyyyzz{yyyz{}~|||||}}|vokliglu}}~~}y{}~~}|~~~~~~~~~~~~~~~~~}||~~|xxuyyvv{|}~}~~~|~|~~~{}~~~~~~~~{{|{||||}}}{wvx{|~|{zxwwvvwwxxxwvuwzzusuvuwxywvtvwwuvwwttuuttvz~~}|{zwxxwy~~{~|{z{{{zzyyyyyz{zyzz{|~~~}}}|ytpmjhhmv}~~~}z{~~}|}|~}}~}~}~~~~~}}}||}~|yyy||yy||}~}~~~~~~}~}~~}{~}~~~}|}||||||||zxwxz}~zyxwvvwwvwwxxwvuvzzusuwvuwxxwwwxxxxyxvttuttvz~~}|{zwxxwy~}{~}|{{{zzzzyyyzz{zzzz{|}~}zwutsqppomkjilkkpv}}}}~}}}}z|~|{}}{|~~~~~~}~~}|}|{~}zz}{{}~|}~}~~~~~~~~}~~}|~|{}~}}}~~~~~~}}||||||{{{z{~~}yxwwvvwwuvwxxwwvvyyusuwwwvwwyxwvuvwvvvusuttvz~~~~~{zwxxwy~|z||}|{{zz{zzyzzz{{zzz{|}~}yuqnlkmmmmnnnoqqrtx{}}{{{z{{{{{|}{|~||}~~}}}}}~}|}||}~{{|{|~|}~}}}~}~~}~~}~|~|z||{{{|}~~~~~~~~}}}}||~}{z}~||zyxwvvwwuvwxxxwwuyyusvxwzyvvwwussuvuvxxvuttvz~}~{zwxxwy~|yy{||{{{{|{zzyzz{{{zzz||}}zxvuuwwxyz{{{wwwyy{{{|{zzyz{|{}~}}~}~~~}}}}||}~~~~||}|{~||||}~|}|}~}|}}~~~}~~}~|~}{||z{{||}}}}~~~}~~~~~~~~}}}{~zx{~}|{{zywwvvvtuwwxxxwuxyusvxxzywuutuuvvvvvwxxuttvz~~}{zwxxwy~~{yvy{|{z{||{{zzzzz{{{z{{|}~|{{{{{yz|}}|{zz{|||{{{~}|{{{}~|}~}~~|||}}}~~~~~}}}}}}||}}}yy{~}z||}~}||}}~~~~~|~|~{||z||||{{{{}}}~~~~~~~~~||}{~~~}{~yvy||}~~~~}{z|{yxwvvvtuvxxxxwtxyusvxxwwwussvxxvuutssuuttvz~~}|{zwxxwy~~{xux{|{z{}||{zzzzz|{{zz{|}~~}|{||}}~}zx{|~~}||}}||}|}~}}}~{||}~~~}}|}}}}||~}~zy{~|xx||}~}|||}~~~~~|}||}z{|{{{zyyyyy||~~~~||~|}~|zy|}{{|||{zyyxvuttuuvvvvvvyxvvwxxxuuuuuuuvttstuutstssvz}~}}{z~|yzzwx|~}{{{{{{||{{{{{{{{zzzz{{{{|}~~~~}|~~~~~~~~}|{zz{|}}}}~~|}~}}}}}~~~~~~~~||}~~}||~z{{zz||}}}}}}}}~~}~|}}|||}z{|zzzzzzz{{{{|}~~~}zyz|~|ywxzz{zyyxvuutuuuvvvvvxwuvxyyyvvvvvuuuvuttuvutsssvz}~}}~~|zy~|yzywy|~}||{{{{{{{{{{{{{{zzzz{{{{}}~~}}||||||||~}||}~||}}~~~}~~}}}~~}|}~}}}|}~~~~~z{zzz||}}}}}}}}~~~}}}}|~|||y{|zzzzzz{{|{zzz{}}}~~|}~~|||{z~}zvtuwyzyyxwvvuuuuuvvvvvvutvz|{zxxwwvvuuwuuuvvvutssv{}~}|}~~|{y~{yyywy|~}}}||{{zz{{{{{{{{zzzz{{{{}~~}}}}}}}}}~~}}~~~~~|}~~~|{|}|||||}~}}~}zz{zz{~|}}}}}}}}}~~}~~}}|~|||y{{zzzzyzz{{zyxxy|}}~~|{|}~||}{|~~zxutuwxxyyxwwvuuuuuuuvvwussw|}{zyyxwvvuuuuvwwussssvz}~}|~~}y}{xyxvz}}~~}|{{zz{{{{{{{{zzzz{{{{}~~}~~~~~~~}}||}~|}~~~~}|~}}z{zzy{}|}}}}}}}}}~~}~~~~}|~|||yz{yzzzyyyyyzyxwx{~~~~~~}||}~zy|~~}wvvwxxwvxxxwwvvuvvuuuvwwurrw~~zzzyxwvvursuvxvsqtssv{}~}|}~}x}~zxxxv{}~~~~~||{zz{{{{{{{{zzzz{{{{}~~}||}}}}}|~}~}}}}}}~~}|{}~~~~~}{|}~|{{{zzz~|}}}}}|||}~~}~~~~~|||{xzzy{{zyyyyyzyyxyz|}~~~~~}{{~}~{wwz|~~~~}|uvvxyywuxxxwwvvvvvutuvwxvssx~yyyxxwwvvrstvwvsqsssvz}~}{|}~~}{zx|~zwxwu{}~~~~~}}||{{||||||||zzzz{{{{|}}~~}}|{{{{{{{{z{{|||{z{{{{zzyy{|~~~}|{~~}~~}}}}}{{z}~}|}|{z{~|}}}}||||}}~}~~}}~~~||{xzzy{{{zzz{{zzzzzz{{||}}}}|{~|zyz{||~~{xux{}}~~|{uuuvxxwuxxwwwwvvwvuttuwxwtsy}wwwwwwwwwutttvuustssvz}~}|}~~~|zyw|~zwxwu|}~~~~}}}}}|}|||||||||zzzz{{{{{{|}}|{{zzzzzzzzzz|}}|{z|||{{zzzxz}~~~~}~~~~~}||||}{zz|~~~}|{||}}}}||||}}~}~}|}}~||{xyzy|{{{||}}z{{|{zyyz{|}}}}}~||}}}~~~|}}{tw{|}}}|{vtssuvwvxwwwwwwvwvuttuwxxuty}uvvwwwwwwxvtstvvwsssvz}~}~}{zw|}zwwwu|}~~}~~||}}}}}}||||||||zzzz{{{{zz{||{zz||||||||z{}~~}{z~~}}wy|~~~~~~~}~~~zyz|~~~}|||}}}|||||}}}}~}||~}~~}~{yzz{||yz|}|{}}zy{||}|{{}}|{}}{~~}|}~~wwwwy|~~~}|{utsuxyywuuvvvvvvvvvvwwwwrsw{|yxxxvuvwyyywtrrsurrsw|~}|}~~~|zyz{vvxxz}~~}{{{}}||||||||{{{zzzyyz{}~~~~~~~~~}wy{~~~}||||~~~~}}~~~}~~~|{|~~}}}}}}|{||||||}}|~}{||}~zzzz|}|zy{{z{~}zxz{{|zyz|}|z}}}~~~~~}}yxwxy{}~|zyutstwxwvvvvvvvvvvvvvwwwwrsw{|yuwyyxwwwzxwvwxyywsqu{}zxyz{{zxwy{vvxxz}~~}|{|}}||||||||{{zzzzyyz{}~~~~~~~~~~~~~~~~~~~yz}~{|}~~~~~~~~~}}~~}~~~~}~|{|~~~~}}|{||||||}}|~}{{{|~{yzz}}|zzzzy|~~zxyzzzyyz|}}{~~}}}}}}}}zyxwyz|}}zywvuttuvutvvvvvuvvvvvvwwwwstw|}zuwyyxwwwzwux|~wqsz}xvwyzzyxwy{vvwwz}~~}|||}}||||||||{zzzzyyyz{}~~~~~~~~~~~~~|}}~z{~~}~~|{}~~}|}}~~}}}~~|{|}}|{|||{||}}|~~}}~|{|~zzzz|}|{zzzyx{{yx{}}zzy{~~}}}~~~~yxwwxz|~}{yxvuuttttsvvvvuvuuvvvvwwwwstw|}zxxxwuvxzxutx}wvz}}{wyz|||{zy~{uuwwz}~~}}}}}}||||{{zzzzyyyyz{}~~~~~}}}}}}}}{||~y{}~~}|}~~}}~~}|}~|{|}}|{|||||}}}|~~~~~~~}~~~}}~~{yzz{{{{{zzz{}}yxyzy|{{}~~~~}~||||}}~~wvvvx{~}}~}}{zyvvvutssswvvvvuuuvvvvwwwwsux}~zwwwvuuwyussx|z{~yz}~~}|x~zuuwvz}~~}}|}}}||{{{zzzyyxxxz{}~~~~~}}}}}}}}{|}}xy|~~|}~~~~}~}~~|{|}}|{|||{||}}|~~~}|||}~~~~~~~~zzzz{zz|{zyz|~}zwxyx|{|~}~~}~~~~~~~~{{z{zzzzvuuvx{~|}}}||zzuvwvtsttwwvvuuuuvvvvwwwwtux}~{suwwvuuuutuz|{}z{}~~~|{x~zttvvz}~~}|~~}}{{zzzzyyxxxxz{}~~~~~~~~~~~~~|}}~~wy{~~|}~~~~~~~~~|{|}}|{||||||}}|~~~}}~}|}}~}|~~{yzz|{{||yxyvyzxx{}}zz{~~~}~~}~}}}}}}}}}||{zyyxwvvvxz|~}}~}|zyxuvwvutuvwwwvvuutvvvvwwwwtvy}~{rtvvutttvxz~}|||||~}|zw}yttvvz}~}|~~}||{zzyyyyxxxxz{}~~~~~~~~~~~~~~~~~~~~xy|~~~}|~~~~~~~~~|{|}}|{||||||}}|~}|||{{|~{yzz}{{}{xww{}}ywxyxyxz}~}~}}}}}}||~|{zzxwvvwy{|}{ywvtvxwutvxwwwvuuttvvvvwwwwtvy~{tttsrrtvx{~~}zx}~~|{w}~yttuvz}~}|~~}}{{zzyyyyxxxxz{}~~~~~~~~~}y{}~}}}}~}~~~}|}~~|~|{|}}|{||||||}}|~~}~||{|~|yz{y{{{zyxxx|}}{yxy{|yxy~~|}~~}|~}|}}}|||{{{{{{~}|{yxwvuuuuvvwxwvvuuuvvvvvvvvvvttv{}yuvvussux~~zz{{{{||{}~~|zxz{|zwuvxy~~~}{}{zyz{||{zxxwww{|}~{{|~}~~~~~~~~|}~~}|~}}}~~}|}|}|z{}||||||||}}~~||~~}z||{z{{{zzzz{||{xxy{{yx{~~~|~}}}}|{zz{{|||}|{zywvuuuuuuvvwxwvvuuuuvvvvvvvvvttv{}yuvvussux~zzzzyyyy{}~~}zxz{|zwuvxy~~~}{~}{zyz{|{{yxxwwx{|}~{z{~|||}~~~}}}}~~~~~}|}~}|}~|{{|||||||||}}~||~~~{}}|zzzzzzzzy{|zywyzyyz}~~~~{|~}~~~~~~}{yxyz{}~{zyxwutsuuuuvvwwwvvuuuvvvvvvvvvvttv{~yuvvussvx~}}zzzyxwww{|~}{yz{|zwuvxy~~}{}|{zzz{{{zyxxxxx||~zyz}}}~}~~~~}}~~~~}}}}~~}}}|}}|}||{||||||||}~~}|~~{|}{{zyyyyxwxz{{yxxzxx{|{~}{|~~}~~zxwxy|~||zyxwuuuuuuuvvwwwvvuuuvvvvvvvvvttv{}yuvvussux}z{||{zzxxvvz|}~}{yz{|zwuvxx~~~~}{||{{{{{{{zyxxxxx|}~~yyy}}~~~~}~}}|}~~}||}}~}}|}}{{|}|z|||||||||~~~}|~|y{{z{zyyzyxwwy||{yyzyy{|{}~|{{}~~~}~~}{xvxz|~|{yyxvuuuuuvvwvvuuuvvvvvvvvvvttv{~xuvwussvx~{xy{}}|{zyxxy{}~}|zz{|zwuvxx}~~~~~~}{{{{{|{{{zzyxxxyy|}~yyz|}||}}~~~}}}}~~}}~}}|}}{z|}|z|||||||||~}~~}~{yz{y{zyz{}{zvz}~|{z{zz{~~~~}z{{{{}}~~}~~}~~~|zwx{}}|{{vuuuuuuvwvvvuuuvvvvvvvvvttv{}yuvvussux~|yxyz}}||{zyyy{}~|{z{|zwuvxw}~~~~~~~}~}{yz{|||{{zyyxxxyy}~~~zzz}}}}~~~~}}}~~~}}~}|}~}|}~||{|{z||||||||{}~|}}~}~}z{|z|zyz|}|{wz~}||}{{}~~~~~~{{|z{}~~~~~~~~~}}}~~}|yz|~}|{vuutuuuuwvvuuuvvvvvvvvvvttv{~yuvvussux}{yyzyyzzzzzzzzxz}~~}{z{|zwuvxw|~~~~}~~}~~}}}{yy{|}||{yyxxxxyz}~~{z{~~~~~}}}|}~~}|~|z{~}|}~~}{zz{||||||||{}~}|}}~~{}}|}{yz{|zxw{~}}~|z|}|~~|zz}|{zz{|}~}||}~~~z{|~}|{vuuutuuuwvvuuuuvvvvvvvvvttv{}yuvvussux|yxy{zyxxxxyyyyxz|~~}{z{|zwuvxw|~}~~}}~}~~}}}{xy{}}}|{yyxxxyyz}~~~{{{~}||}}~~~}}}}~~}{|}}}}|}~|}}~~~zyz{||||||||{}~~~~{}}~|xyyyzzzzzvux~}}~{{}zwxzz|{{{{{|}}{||{~}z{|}~xwutttvwxwvutuuvyxxwvvuuttw{|vutsstuvzzzzzxwvttuuwwxxzyy||xy~~yvwxvx{~~}}~~}}}}~~}yz{}~~}{zyyxxyz{|{z{~|}~~~~~~~}~~|{|{www{}~~~}|z{{{{{{{{}}|}}||zzzzzzzzwvy~|{}{{|{xyyz{~||||||{{z{{{}|{{|}~zywuttuuwwvvuuuuxxwwvuuuttw{|vvtsstvvzzzzyxwvsstuvwwxzyz}|yy~~yvwxvx{~~}~~~~}}}~~}yz{}~~}{zzyyyyz|}{z{~}~~~~}}}~~~~~|{||xyy|~~~}|z{{{{{{{{|}~|}}|}}||{zyyxwy~{z{{{}|yyzyz|}}}}|{zy{||{}~||}}~|{ywutttvvvvvuuuvvvvuuttttw{|wvuttuvwzz{zzxwvrrstuuvvyy{}~|yy}yvwxvx{~~~~~}}~~}z{|}}~}|{{zzyz{|}{z{~~~~~~}}}}}}~|{||xzz}~}~}|{|||||||||}}~||~|~|zxwwvy~|{}{{||yz{z{}||}~}{zy{{z{{}}}}}}~~}{yvussuvvwwvutuuuuuuuuttw{|xwvuuvwx{{{{yxvurrrssttuxy{~~~{zy~~yvxxvx{~~~~~~~}{|}}}}}||||{{z{|}{{{~~~~~~~~}}~~~~~|{|}yzz}~}}}~|{|||||||||||}}}}}||~~~{xwwvy}}{{}~~~~~{yz||}~z{}}}|{zyyyyyy{|~~~~}{yvtstuvwwvvuuuuuvvvvttw{|yxwvvwxy{{|zzwvurrsrssttwy|~~}|{y}yvwxvx{~~~~~~~~~~|~}}}}}~~~}|{{|}}{z{~~}}}~~~~~~~}}}~~~}}}}}~|{|}zyy|~}||~~}||||||||||||||||||{~~|{zy|}{{|~~}}~~~zy|}}}z{||}}|||{{|{z|~}zxutuuuvvvvvvvvwwxxxttw{|zyxwwxyz||{{ywutsssssssswz}}|{{y~~yvxxvx{~~~~~~~~~|}~~}~}}|}~}}|}}~{{{~~}}}~~}}}}}}||}}}}}}||~|{|~{yy|}|{|}~}|||||||||}}}}}~~~~{{}~~}{|{{}~{z|~||}{{{|||}}~}}}|{}~|ywuuuuuvvwwwwxxyyzzttw{|{zyxxyz{|||{ywuttttstsssvz~}{{|y~~yvwxvx{~~~~~~~~~}||~~}}||}~}}}}~{z{~}~~~~}}~~~}||}}}}}}}|~|{|~|zz}|{{|}~~}{{{{{{{{}}~~~~~~{{}}|yy{{|}{}}|{|{{{{{|}}|zz{yxz~~~}zxvvuutuvwxxxyyz{{|ttw{|{{yyyy{{}||{ywusttttttttvz~}{{|y~~yvwxvx{~~~~~~~~~~~}{|}~~}||}~}}}~{z{~|}}~~~}}~~~~~}}}~|{|~|{{~|{z|}~~}{{{{{{{{}~~}}}~~~~~}{{~}}vvx~|}~}}~~|z{}{}~~~~}{yxwqsuvuvxz|{xwvwyzyxy|}|zwuuw{}~}zxvvvwwvusssstwy{}}|{yy}~zxyzyy|~~~~~}~~~~~~~~~~~~}}|~}}}}~|||}~{z{~y{~~|~|z||||}~~}||~|{|~z{|{~}{{||||{{~~}}~~}}~~{{~~y|~~}|~{{}{}}||~}{|~|~~~~}|{zzvvvtssvy}|{yyyyywvw{}|zwuuxz}|{ywvvvwwvuttttuxz|~~}|zx|}ywyzxy|~~~~~~~~~~~~~~~~~}}}}~}}}}~~}|}}~~{z{~|}~~~~}}|||}~~}|~~|{|~z{|{~}||{{{{||}}~}}}}~}~{|~~~}uwxuqpuz|~{{|{{}~||~~~~~}}|||{zxursux~~~}|zyxttvz~|zxvvwz{yxwvvvvwxwvuuuvvyz}~|{x|}ywxyxy{~~~~~~~~}}}~}}|}~~}}}~~~{z{~~}||}~~|}~~~||}~~|}}~|{|~{{|{}|}|{zz{|}||~~}{|}|~~~~~~{{~~~zwywogejpz}y{{zz|~|{}~~}}|||||ywvxz~|ywssuy~~|{xwvxyzwvvvuvvvxxwvvwwxxy|~~}{zw||yvxyxy|~~~~~~~~}~}}~}}||}~~~}~~~{{{~~}||}~~}}~~}}|}~~~|{|{{|{}~|}|{{z{|}{{}~~~z|~~~}}}}|}{|~}~~y{zwmc`fmz~|yz{yz{{yy|~}{zyz|~}}~}{yutvz~|{zxxwxxuuvvvuuuxwwvwwxywx{|}|zyx|}ywxyxy{~~}}}~}~~~~~~||||}~}}~~~~~{z{~||}}~~~}~~~~|{|{||{}~{{{|{|{|{z{|~~~~~}y|~~~}}~||}{{}}~~~yyxtledjp|}yz{zz|zxx{~|zyxx|~~}|xxx|}{{zyxwwvvwwwvvtswwvvvwxywy{}}|{yy}~zxzzyy|~~~~}}}~~~~~~|{{{|||}~~~~~{{{~z{}~~}}}~~~|||}~~}|~|{|~||}{}~{{{||||{{{||}}}}|}~~~~}}z}~}|}~}~{|||}~~wwuqlhinsz}z{|zz||zy{~~}~~}|zyyx{~~}}}~|{|~|{{{zywvuxxyxwusrvvvuvwxyz{}}|z~{y{|zy|~~~}}}}~~~|{{{{|{|~~}~{z{~{|~~}{|~~}|}}{{|||||{~|{|{zz|||{|~{||{{{{||||}}}|{z||}}~}}||~}{{}}~{{{|}}}vxupljkorv|~z{|{{|~|z{}~}|}~}}}|{{zzz|}~}~~}{{|~~~~|{{{zywutyyzyxusqvvuuvwxy|~~{|z|}{y|~~~|}}~~~|{zz{{z|~~~~{z{~}}~~~~}|y}}|~~}||||}~~|{|~}xvw||}{|}z}|{zz{|}}}}}|{zy{{|}}||{}~|yz|~}{~{{{{}}ywtoljjkkn}|z|}|zyyz{}~~~~~~~~~}}}{|}~}~~}{yz|}}}}}~|{{|}|xvyyywtrrsuuvtsrtvw{~~}yz~zxyyxw}~~~~~~}}|||{{~~~}||~~~~}}}}~~}}}~|||||||||{{}|yz{|}}||{~{|}}~||{{zz{{|{~~zy{|{}||||{{{}~~||}}}||~~}~y}~~~}yxvrnjgggn|}zy{}}{yzz|}~~~~~~}}|{{}}}}|{z{}}~}}~|{z{|{xuyzzxusssvwwvttvxy|~}}~{z~~zwyyxx}~~~~~}}|||||~~~}}~~~~~}}}}~~~~~~|||||||||{{}~|z{|}}}|{}z{}|~||{{zz{{|z|}}|}{z}||||{{{|}~||}}}|}}}}~}y|~~}}zyyuogccdn|}{yy{}}|zz{|}~~~~~|zy|}|||{{|~}~~}~~|{zz|zxv{{{yvttuvwxvuuwy{}}||~~|z~zxyyxx}~~~~~~~~}}||||~~~~}}}}}~~~~~~}||||||||||{{}~}yz{|}}}|}y{||}~{|{{{{{{||z{|~|{{}||||{{{z}~||}}}|~}}~~|}~}}}zzzumd`bfq|{zz{{{{{||}}~~}~}|{}}|}}|||~~~}~~}{|||zx{||zwuuvuvwvtuwy{~~}}~|z~~zwyyxx~~}~~}}||}~~|||}}~~~}}}~}}|{|||||||||{{}|zyxyz}}~~~|yz|{}~{|{{{{{{|{xz{{{~}||||{{{y|~||}}}|}}}}~z{ytjb`eju~~|{zz}}}}~~~~~}~~~~|{|~~~~||}~~}}}|~}|{|}}zxuuvstutssvxz}}yz~zxxyxy~~}~~}}}~~}|||}}}~~{{{z{{|}|||||||||{{}|vtsvwz|~~|yz|{|~{{||||||{{{|}zz}}||||{{{y|~||}}}|~}}|}z{xrjcbfku|||~~~~~~~}~~~{y{~~}|}~~}|~}}}~}||}|{wuuvrsttstvxz~}xz~~zwyyxy~~~|~~}}~|||}}}}}{zyyy{|}|||||||||{{}ztqquwy|~}zz}{}~{{||||||{|}zz}||||{{{z}~||}}}|~}}||}||z{xsmhfffs~~~~}}~~~{z|~~~~~~}{}~{z{|{z{||zwttustuttuwz|}zz~zxyyxy~|~~~~}}}||||||||{|||||||||||||{{}{usttvy{~}z{}|~|{||}}||{~|z}||||{{{|}~||}}}|}~~}|||xw~|zzxtqmhdbp}~~~~}}}~~~~~|||}~|~}{}}yxyzzy{|{yvttutuvvuvy{~~}~|z~zwyyxz}|~~~~~}}}}||{{}}~~~}{{|||||||||{{}}wvxtux|~~{|~}~|{||}}||{~}||||{{{}~~||}}}||~}|}}|||~xsv~{syvtsmedfr}~~~}|~~~~~~~~||~~~}}}~~}{yxxw{{zywvutwvutuwz|z}~~~{z|zzzx{~~~~~~~}~}~}}}}}}}}~~}|{{{{{|||}}~~zzz|}yvuuwxz~}}||||{}|{|z}|yy{~~}||}~~}}}~}}~~{}~toqz|zzwtqkeeis~~~~}}~~~~~~~}|~~}~~}{zzzyxwvvuvuttuxz|z}~}}~|yz|zzzx{~~~~}~~~}}}}}}}}|}}~}|zy{{{||}}}zzz|}yvuuwxz~}}~~~}~~{|}}}|}~}}||}~~~~~}~|~~|}~}}rknw|x|{wsojeglt~~~~~~~~~~}|~~~|zxxxxwwwwttttvx{|{~}}}{xz|zzzx{~~~~~~}|}}}}}}}}||~~~|zy{{{{||||zzz|~xvuuwxz}}~}~}{y|{|||}~~}}|}~~~~~~~|{}|}~y~~tmox~uu{|wrnifiow~}}}~}}~~~}~~~~~~~}}~{wwwwwwwwwtttuvy{|}~~~|xz|zzzx{~~~}}~~~~~~~~|}}}}}}}}}}||{|{{{{{{{{{{zzz|}yvuvxx{}~~~~|~w||~}}}}}~}}}~}zy||}~~}x{~|xrt{zvw|{vrojgjqx|{z{|{{|~~~~~~~~~~}~~~~~~}~~{wvvvwwwwxttuvxy{|~}yz|zzzx{~~~~}~~~~~~~~~}}}}}}}}}}||||{||{{{{{{{{zzz|~xvuvxyz~~~~}y{~~}}}}~}}}~~yxz}|}~~{vy{{}zvx|~yy{}~yurplikqz}{{|||{}~~~~~~~}}|}~~~~~}~~~~{vvvvwwwwvwwxyz{{}}zz|zzzx{~~~}}~~~~~~~~~~~~~~}}}}}}}}}z|}~}}{z||||{{{{zzz|}yvuvxy{~~~|{{}~~~|~~~~}}~~~~~zwzz{|}~~{wttwxz{{vuvxyy|ywxyyxvtsrojlp{|z{|}||~~~~}~~~~~~~~~~~~}~~~~~}~~wwvvvvvvxyyzzz{zz}~~~}zz|zzzx{~~~~}}}}}}}}}~~~~~~~~~~~~|}}}}}}}}z{}}~}{z}}}||{{{zzz|}xvuvxy{~~}{z{}}~~~~~~}~~~~zxyyw}}~~|vsrutrqswwuxurrstsqttttttsrusstpllp{{yz|}}|}~}}~~~~~~~~~}~~~~~~~~~}}}wwwvvvuuzz{{{{zzx{}||}|yz|zzzx{~~~~~~~}}}}}}}}}~~~~~~~~~~~~}~z}}}}}}}}|||||||}~~}}|||{zzz|}yvuvyy{~~~~~~~~~~}~}}}~{xywu}}}~~~rnqtsomortutsrqqrrqpontrpnmnpqopqrsuuv~|zzz|}}~~~~~}}}}~~~}||}~~~~}}wsuwwwx|{yvy}~}|~|x||y{{z|}~~}||~~~~~~~~~~~~~~}}}}}}}}{|}}}}|{}||||{{{|{z{{wuvxy{~~~~~}~}}}|xwxxy||}ypnrwwuoprssrqptttttsrqrqnmlmnommnpqrstyz}~}}~}~~~~~~}}}}~~~~~~||~~~~~}}wstvvvw|xty~~{{zxz~{xyzx{}~~~~}|}~~~~~~~~~~~~~~||||||||{|}}}}|{}||||{{{|{z|~zvtwyy{~~~~~}}}xxxxy|zz|~}vtx{|zvwxxxwvuwvvuuttttsqpopqqlmnopqrsrsvy}}||}zz{{}~~}}}}~~~~~~~~}|~~~}xstuuuvztz{yyyz}~zwxywz}}~~~~~}~~~~~~~~~~~~~~~~||||||||{|}}}}|{}||||{{{|zz{|xvxzz|~~~~|}~}yxxwy{zw|||}~zwxyxwttuuuuuusrqpooppssrqpppptsrqpnmmnoqtx|}zyxxwwxyz|}~~}}}}}~~~~~~}}~~~~}ztsttst~z|~|zzz{{xyzx|~~~~~~~~~~~~~~~~~~~{{{{{{{{{|}}}}|{}||||{{{|{z||zy{{|}~~~}~yyxwx{|y{{~}|}~zwwwwvxxxxyz{{{zxvvvwxrrrqqpooqqpppooomnoquy}}zyxwzzz{}~~}|}}}~~~~}~~~}|uttsrs}|}~~|z}}{{|{~~~~~~~~~~~~~~~~~~~{{{{{{{{{|}}}}|{}||||{{{|zz{}{z{{|}}{yxwxz~~z{zz}}~~~~}zz{}zz{{{yxwrrqqppoonnnprwz}~~}|||{||}~~}||}}~~~~}~~~}~wttrrs|z||{~~{}}|}~~~~~~~~~~~~~}}}~~~~~~~||||||||{|}}}}|{}||||{{{|{z|~{yy{{|}}~{zxwwz{}yzxxyx{~~~~~zxxxxywsqprtv~|xtpmlonmmpswy~~~~~}}|z{{|}~|~~~~}||||~~~~~~~~~~}xutrrs~}{}}{|}}{||{}~~~~~~~~~~~~~}|}}~~~}}|~||||||||{|}}}}|{}||||{{{|zz{~{yxzz|}~~|zywwyx}xxwwxv{|{~~}||}~yusrr{xurqsvx}~~|z}|zyyonllmptv~~|{yx{{{|~}}~~~~}||||}~~~~}~~~~}yvtrrs}}~zz|||yz{y~}~~~~~~~~~~~~~}||}~~~}}||~}}}}}}}}{|}}}}|{}||||{{{|{z|}{xyz{}~~~~~~~~~~}|zxwwywxxwyzx}zy~{uqrvyuxyvrrx~~}tqstvy}~|yrnoqppq~}|||{|}}~~~~||}~~~}}|{{{|~~}}~}}{yyyz~}}|tqrsry~~}yz~|yzzw~~~~}~~~}}}}~~~~~{~}}}~~~}||{{||}{{{z{||}{{|||}}}}||~}yxxuwz}~~}~~~~}yyxvy~xyyvvy|}}|{|~{uqruxwyyuqry}tqstux|{vsnknu~~}}{{|}~zy|~~}|}}~~~}}||{{|~~~}~~|zxxyxxz}~}~vrrsrx}{|~|xz|yzzx~~~~}~~}}}}~~~|}}}}}~~{{{{{|}~{{{{{|}}{{|||}}}}|{}|xvvvxz}~~}~~~}|yzywx|xxywvy|}}}|}~zurrtuxyxtprz}tqsuuwz}xpls~{{{{}~{tty~~}||}~~~~}||{{|~}~}{xwuutxwxz}~~}xsssrx|z{~}yz|zzzx}~~~~~~~~~~~~~~}}}~}}||{|}~~~{z{{|}~|{{{{|}}|||||}}}|{z|}xvuwx{}~~~}~~~|~~|||zyzyxywvx|}~}}~~~||}~}|~~~~ytssssyyxtps|{tqsxwwz}~~~}xv}||{||~ypow}~||}}~~~}}||{}~}|zwutttuwwwz}~~~}ztrrrw~{||{}z{zx}~~~~~~}}}~~~~~}}}}~~||{{{||}~||{{{|}~||{{{|}~||||||||{zz|}ywwyz{}}~}}~~|~|zzwxwwx{}~}}~~{yyz{zy|}~~}~~wttttswyyurt}zrpsyxxy}|{|~~~~}~xoov}~|||}~~~}|||}}~~{yxwuuuwxwvvy}~~~}~{tqqrv~~{z~~|{}{{{y}~~~||}}~~~~~~}}|}}}|{z{{||~}~}|{{{|||||{|}}~||||||||{zz|{{{{{||}}}}~~}}}y~{wwxxx{}||}~~}|zxwxyyxz{~~||~{usuvusuxyxuv}xpnqxxwy|{{~~zsrw|}{|}}~~~}}||~}}~|zxwwwwxxyvuuy|~}}}tqqru~}yxz|{y{~{|{y|~~~|}}}~~~~~~~~~~}}|}~~~}}|{z{{|}~~}|{{z{}||||}~~}}}||||||{z|}|}|}|}}}}}~}}{v{|vvxxxz}{{|~~~|zwwxyxwz{}~}||~}xssvywtqv{{yx|}vnmpuuvy}|z~}xwz||{||}~~~}||~}}~}}~}{xwwxxxwwvutux|}}vrrtu}ywyzyw|~|||z|~~~}}}~~~~}~~~~}}|||}~~}~~~}|{{{|||~~~}|{{{{{}}|||}~}}}|||{{}|{}|{|~}}}}}}}~~||~{vz|uvxyxz}~{z||zxxyyyxz{}~~}{|}{vqrwzxuou|}{y||umlorsuy~{y~}~~}|{{|{{|}}~~~}}|~}}~}}}}zxwxyyxvtrttux|}}wssuu}|yz{zx||||z|~~~}~~~~~~}~~~~~}|||}}}~}}~~~}|{{|||~~}||{{||}}}|||}~}}}|||{{}||~{zz~~}}|}}}~~}~~|{}|w{}uuxyxz}~zz|}{xxyzzx{|}~~}{|ywuttuwysuwwvwz}}|zwtqontux{{yx||{{{~{yyz{}}~~~~}}~~~~~}}}}~~~~~|{zwyzywvvwuuvwz|~}~~~~}~~|{||x{}|{z{}|yww|~}~}~~~~~~~~~~~~~~~~~~~}}|||}}~~~~}}}|{{{{|}~~~~}}}|||~~~~~~~~}|||~~{zz}|{{zz{}|z{~~~~~}}~zzz|~wuvy{|~~{{~zwxzyy{y|~}{{{~yxvuuvxy{ytpot}~~|zwvtsz{}}~}y~|zyz{|}}~~~}}~~~~~}}}}~~~~~|{yywyzywvvwuvvxy|~~~~~~~~~~}y{}|zy{~|yww|~}~}~~~~~~~~~~~~~~~~}}~~}~}}|||}}~~~}}}}}|{{{{|}~~~~}}}|||~~~~~~~~~~}|||||{zz}|{{zz{}|z{~~~~~|{{{{||}}}wvvvwz|}}ywwyyz|~~{{~zwyyyy{z{}}xrqwzvxyzxyyxxyz|}}~~~~}~~~~}}}}~~~~~|{yxwwxyywvvvuuvwz|~~}}~~~~z{}{yx|~|ywv|~~~~~~~~~~~~~~}}}~}}~}}|||}~~~}}|||}}|{{{{||}~~~}}}|||}}}}}}}}|||}|{{z{zz}|{{zz{}|z{~~}|}~~|{z~}}||||{}yxxwwwwwvvwy{{yw|~}{{~zwxzxy{{{||~~}{vpnptvvttuwy{}}~~~~~~~~}}}}~~~~~|{xwvuxxxxwwuuuvvxy|~~}}~~z|}zxw||xvv{~~~~~~}}~~~~}||||~~}}||{|}}|{{{{||}~~~}}}|||}}}}}}}}{|}~~|{z{zz}|{{zz{}|z{~}~~}xuxxxyxxxxuwy|}|xv|~}z{~{wyyyxz{{zz{}}yvtuuuvvuuuoruutrstssttvvwwtttttsssrssrqppqqqqqqqqqpnllnprrpqtwz}~~~~~~~}}}}~~~~~~~}{xwvvuxxxwxvvuuuvwz|~~}}~~z|}zxw}|xvu{~~~~~~~}}|||~}}|}||{}}|{{{{||}~~~}}}|||}}}}}}}}|}~|{{zz}|{{zz{}|z{~~~~}uquvwxyxxxxxyz|{yw|~|z{~{xxzxxz{zyyz|~~{wurqqrsttsrqopqpnnpspppqrsssrrrqqqqqnoqponpqppoonmllqqpqppoonqtx{~~~~~}}}}~~~~~~~~}|zxvvvwxwwwwwutuvvxy|~~~~~~~z{}{yx~|xuuz~~~~~}}}}}~~}}}}||}}|{{{{|{|~~~~}}}|||}}}}}}}}~~~~}|{zz}|{{zz{}|z{~~wsvwwxxyxxxwvwxzzy|~~|z{~|xyyxwyzzyyz|~|yusrstnoqrrqonpppmklorpppqqqrrppppqqqqmprqnmoqtsssrrqqtuusrpppoqvz}~~~~}}}}~~~~~}}}|{ywvvxxxwvvxwusuuvwz|~~}y{}|zy~|wutz~~~}}~~~}}~~~~~}}}}}|{{{{|{|~~~~}}}|||||||||||~}||||}{zz}|{{zz{}|z{~ywwwwvwwwwywuuwxyy|~~{z{|yyywwyyyzz|}~~usolkmprpqsuusqpqrsqonpsuuuuuuuuppqqrrsspsusomorpppqqqqqtttsqqrsprw{~~~}}}}~~~~~}}}|zxvvwyzxwvvwwusuvvxy|~~~~|x{}|{z~|wutz~~~~||}~~}~~~~}~~~~~}}}|{{{{|{|~~~~}}}|||||||||||~}|zz{|}{zz}|{{zz{}|z{~~~zyxxwwwxxy|zwwwxxw|~}{y{~|yyywwyyyz{|}~~zwsonoqrurppsutrsrppqrrqrtspnopqqrstuuuuvurqrrrrqqqrrsstssssssrrrvz||||}~~~~~~~}~}}}}}}}}~~}{ywxxxxxxxxxyyxxwvuwvuuw{~~~~~}|z|}{{|{wuuy~~~~~~~~~~~}}}}}}}}}}}}}~~~~|{zzz}{{}~}}}||{{}}}}}}}}~~~~}}}}{zz}~{zz~|xxzz{{wwxy{||zxzyxwwxyy~~~~{{~{xyzyy{xwwwy{~~ywtrpppqsqnortrqqqpppppqqqqpnnpsuvwxxwvvtsqqrtuussssrrqqqrrrqqppruz}||}~~~~~~~}}}~}}}}}}}}|}}|{yxvwwwwwwwwxxxxwwvuwvuvwz~~~~~}|z||{{|{wvvy~~~~~~~~~~~~~~~~~}~~~~}|{zzz}|{}}}}}||||}}}}}}}}~~~~}}}}{zz}~{z{||yz|{z|xvwy{|}}|zzxwwxxy~~}{{~{wxxwwywwwxy|~xwusqppoqomnpqqonoqppopponooonrv}|{yxvtsrqpprsttsrqppqrsqrsrqoopquz|}|}~~~~~}~}}}}~}}}}}}}}{{zyxwwvwwwwwwwwwwxxwvvuwvvvxz}~~~~|z|}{{|{xvv{~~~~~~~~~~~}|{z{{}{{}}}}}}|||}}}}}}}}~~~~}}}}{zz}{{{z{y{}{y~}zwxzz{|}~{zyxwwxx~~}z{}zwwywwyxxyz{}zwsommnopnmnoqonnopqppopnnnooqty}{xutsssqpoooppprqommnopprsronnppuy||}}~~}~}}}}|||}~|||}|}||zzyxwwwwwwwwwwwwwwwxwwvuwwvwxz|~~~~~~~~|z||{{|{ywx{~~~}~}~~~}|{{{{}|{}}}}}}}}}}}}}}}}}~~~~}}}}{zz}|zzzzxy|zy~}zzz{zyz}{zyxwwxx~~}zz}~yvyzzz}}}}~~|xqljkmponnnpppnooppqqponopoqtxyzwtqqqqqqponoonmmnnoonnmnoqpnlmnpty|}}~~~~~~}}}|||{||~~{{{{|{{{zyxxwwxxwwwwwwwwwwxwxwvvxwxxz{|}~~~~~~}|z|}{{|}yxy|}}}}~~~~~~}{{{{|}{{}}}}}~}~~}}}}}}}}~~~~}}}}{zz}zyy{zvvxwx~{yyzzxy{{zyxwwxx~~|zz|}xwy|}}ztommnponnopqonoonpqrqpoppqtxyywtrqqpompooooponllnoopppmoppnmmnosx|}~~~~~}}|||{{{{|}}zzzzzzzzxxxxxxxxwwwwwwwwwxxxxxwwyyz{{}}}~~~~~~|~|z||{{|}zyz|~}|||~~~~~~~}}~~||{{||}|{}}}}~~~~~}}}}}}}}~~~~}}}}{zz}}ywwzxtsvuw}zwvz|{z|zyxwwxyy}~~|yy|}xvy|}}}{zwsponnoopponnnnnopppqporvyywrpoopqpnonnopponponmmmnonnooooooosx|}~~~~}}|||{{{z{{|}xxyxyxyxvwxyyxwvwwwwwwwwxxyyyyxxzz||}~~~~~~~~~~|}}|z|}{{|}{zz{~~~~|{{}~~~~}}}}|~}|{{{||}{{}}}~~~~}}}}}}}}~~~~}}}}{zz}|xvvwvssvtuz|ww|}{{yyxwwxyz~}~{yy|}yvxzz{}}~|yrmnnnopponmnnnmnoqronsxywuspmmnoomnmmnnonmnnmlllmmlmmnoonnnsx|}~~~}}}|||{{zzz{|}xxxxxxxxtuwxyxvuxxxxxxxxxxyyzyyx{|}~~~~~~~~}~~}|~{~|z|}{{|~{z{{||}~~}||~~~~|{{|}~}}}||||~}||{||}}{{}}}}~~}}}}}}}}~~~~}}}}{zz}{wuuttstwtswzx~zyyxwwxyz{~}~~{yy{~~yvwwvvxxy|}uroooooooonmmmlllkhkpw||yvssrqppoonnnmllkkppoonmmlmmmnnnnnkqy~}|~~~}|{{{zzyyxwwwwwwwxxxxwwxxxxwwvvvwwwwwwxyyxy|~~||~~}~~~~~~~~}}~~}}}|{}|{|{|}zxx}{|||}}~~~~~}|||||~}||||}||{{{{{z{}~~~~}}~~}|~}}}}|z|{{}|xvvwvvxxvx|{{{z{xxyyyz{|~~~}~|yy{~}xuvxwxz}~|~xtppppppppttssrrrrpquz|{vrssrrrqqqsssssrrrtttsrrqquuuttssspv|~~~|{zxxwwwwwwwwwwwwxxxxwwxxxxwwvwwwwwwxxyzzyz}~||~~~~~~~~}}~~}~}}|{~|{|{~|zy|~}~~~~~~~}|||}{{|~~}||||||{{{{{{{|}~~}~~}||}~~}}}}|{|{{}}xwvxwwxwvw{}z}z}|zxvwz|{y~~~}~|yy{~}xuxyyz|}{x{zsrrrrrrrrsssrrrqqsux|~|wszzzz{{{{zzzzz{{{}||{{{{zyyyxwvvvw{~~}{ywvuwwxwxxxxwwwwxxxxwwxxxxwwwwwwxxxxy{|{{{~~||~~~~~}|}~~}~|||~|{|{}{z{~~}~~~~~~}}}}zz{|~~~}||||}||{{{{{{|~~}~~~|zz|~}}}}|{|{{}~zxxyxxyxuwz~{yz{xvvy{zx}~~~}}|yy{~}xvx{z|~~{wzxpmmmmmmmmoooonnmmprw}}zyzz{{|}}{{zzyyyx{|{{{{{{{{{{{{{{z|~~|{}|{zywvuuyyxyxxwwwwwwxxxxwxxyxxwwwwwxxyyz{|}}|}~||}~~}|}~~}|~~~|||~||||~|zzz~~~~~~~~~~~~~}~}}}}}}~}||||||{{{{{|{|}}~~~~~~~~~~}}~~}||{{}~{yyywxyxvx|{y~{zyxvwxy}~~~}}|yy{~}yvxzzz}}y{}rknnnnnnnnpooonnnnmpv|~yyzz{||}~~}|{yyx{{{{{{||{{|}~~xz{{xwy{xwwwwvvvwwwvwvvvwwwwxxxxxxyyyxxwxxxyzz{{|}~~}~~||~~~}||}~}||{||~}|}|}~zyxz~~~~~~~~~~~~~~~~}||||}||{{{{{{|}~~~}}|~}}~~}||{{}~{zzwvwyyxz~~|zz}|zwvvxy}}~}|}~|yy{~~yvwyxy{~{|~ulilllllllloonnmmmmnqv|~zxxxyyzzzzzyxxwvvuuuvvwwwstttuuvvsvyywvvwvvvvvwwwuvvwwxxxwwwwxxxxxxxyyyxxxxyzz{{||~~}~~||~~}|{|}~}|}}~}|}|}||}zxw{~~~~~~~~~~~~~}||||||{{{{{{z{|~~~}~~~~~}}~~}|{{}~{zzvuvyyy{~~{|zx{~xwwyxv|}}}||~|yy{~}yvxzz{}}{||vmiknnnnnnnnmlllkkkjnpu{{wqppppooolmmnnoppmnnoppqqrrqonmlknsy{zwvvwwwvvvvvvvwwxxyywwwwxxxxxxyyyyxxxyyz{||}|}~~}~~||~~}|{|}}|{|~}}}|~|}~{yy}~~~~~~}~~~}||||}||{{{{{yz|}~}~~~~}|}~~}~~}|{{}}zyywvwyyy{~|yzyxy}zwxyxw|}}}||~|yy{~|xvy}~{z{zqhhnllllllllnmmmlllkkmsy~{xppponmmllmnpsuvwxxyzz{||{zxuromklry}|ywvyxxwvuttvvvvvvvvwwwwxxxxxxyyyyxxxyyz{|}}|}~~}~~||~~~}|{|}}|{z|}}}}~}|{z}}}}~~~~~~~~}}}~~~~}|||||||{{{{{yy{}}~~~zz{{|~~}~~|{{}|zyyywxzyxz~~~{xxxyyy}ywwxy|}}|||~|yy{~{ww{}xtqonkhnnnmmkkjlmonmllmmqsuz}upppoonnmlnrv{~|}~|~|upmhmuyyyxyxxyyxxwwvvwwwwwwxxxwwxxxxxxyyyyyzzzz{{{{~~~~~~~{|~~}}}}zwwy}z|zxz|}~}|{~}|||zyz{~~~~~~~~~~~}~~~~~~~~}}}}}~}~~~~}|{{{||||{{{{{{yz|||}~~~~~~~~~~~~}~|{{}|{}}{yyvtvwwz~}|x|}yvx}uw}||~xyz|}}wtw|xronnmkhjklkkkkijlmlkjjloqsx~ynnmmnoppmpsx}}|~}vplhmtyyyxyxxxxxxwwvwwwwwwxxxxwwxxxxxxxyyyyzzz{{{||~~~~}~~|}~~~}{{yvvx|{wx}|yyz|}~}|{~}||{~yy{{~~~~~~~~~~}}}~}~~~~~~||{{||||{{z{{{y{|||}~~~~|~~}}~}|{}}{xwyvuvwwz~|yzxuw}}}}~yzz{|}~~|vru|wpllmmlhijjiikmiikmnmkjklnov}~sqolllmnoqv{~~~||~~xpkhmuyzxyywwxxxwwvwwwwwwxxxxxwwxxxwwwxyzzzz{{{{|||~~~~~~~}}~~{yyxuuw{~yutwyxxzyz||}}||~~||{{}yz}|~~~~~~~~~~~~}~~}}~~~~~}}{{{||||{{{{{{z{}||}~~~~~|}}}||}~{xwzwvwxxz~|zxvv~zvy}wojijiiklkiggjmiiikmnljijjmrywuqmkkklptx~~}{}~}~zqjhntyyyxywwwxwwwvwwwxwxxxxxxwwxxxvwwxyz{{{{{|||}}~~~~}~~}~}yvwvutv||wxutuuuw{z{{||}|}~~}||{{z{}~}}~}~}~~~~~~~~~~~~~~}}}~~~~~~||{{||||{{z{{|{|}}|}~~~~~~~~~~~~}}}}}||~~{z{xxyzy{~|~{y{~xqkihgejkjihgikigggjjjiihhjnszzxtpnlllqtz|{|}~|qhhmuyzxyyvwwwxwwwwwwwxxxxxxxwwxxxwxxyz{||||||}}}}~~~~~~~~~}xtuvttv{~|xuwuuvutvz{{{{||}}}||||{{{ywvy~~~}~}~}~}~~}~~~~~~~~~~}~~~~~}~~~||}}~~~~~}}{{{|||{{z{{|||}~}}}~~~~~~~~~~~~~~}~}}~}{}{z|}{|~~{|}ytqomjggghiiihgjigghjjkhggjkmry|xrnjhptz~}~~~~qghntyyyxyvwwxxxwwwwwxxxxxxxxwwxxxyyz{{||}||}}}~~~~~~~}~~~~}wstuutvwz|}{yxwvttvustw|{z{{|}~{{{{z{zzxusv|~{{{{{{{{}|{}~}}~}~}~}}~}}}}~~~~~}}}}}~|||}}~~~~~||{{||{{{{{{||}~~~}}~~~~~~~}}}|}}~~{yww{z|zyyzwsokiggiihfjjjiiijkighjifjq}zupmosz~}}~~~rfhmuyzxxywwxxxxxxwwxxxxxxxxxwwxxx{{{||}}}}}}~~~~~~~~~~~~~~~}wrruuuvvwyyxwwxxustuttw|{zzz|~zzzzzzzzwvvvy}{{{{{{{{zyy{~~}}}}}}}}|||||}~~~~~~}|||}}||||}}~~~}}{{{||{{{z{{||~~~}}~~~~~{~}}~~~|ywvv{y{{|{wtngegiiggijjhghiighjgcelpty~~}nsz}{z{~~~rfhmuyyyxywwxxyyxxwxxxxxxyxxxwwxxx||}}}}}}}}}~~~~~~~~~~~~~~~wqruuuvxxxwustv{vstuuvy}|zzz|~yyyyzzzzwyyxvx{|{|{|{|xwwz}~}}}}}}}}|{{{{|}~~~~~}|{|||||||}}}}~~||{{||{{{{{{||~}}~~~~~~~~~~~~}}~}~}|{~}~{zzz{y~}{zz{|{zyxtnhegiggggggggcddefgghbgov}z|}~~~|{}~}}}~~|zxxzxxxxxxxxxxxxxxxxwwxxyzzz{{{{||}~~~~~}~~~~~|yvuuuuuwxxxxwvuuuuttuvvzzzyz{|}{{zyxwvvw}xvz~{{zyz{{{z~xv||z~~}}}~|{}~|{||||||||||||{|}~~}}~~}}||{{{|}|z{}~~~}}}}~~~}~}||||}~~~~~}~{|~}}~~~~~~~~~~|wojptwwsnkijjjjjjjjmnnoppqqrtwyzywv{}}|zyx{~~~~}zwwyxxxxxxxxxxxxxxxxwwxxyzzz{{{{|}}~~~~~}~~~~~~|yvuuvuuwwxxwwvuvvuttuuv{{{{|~~}|zywvvvz}|wux}~{xwvvxxwv{vu|~}~}}}}~~zz|}|{||}|}|}|}}}||}~~~}}~~}}||{{{|||z{}~~~}}~~~}}}|}~}zxy}~~~{{~~~}||||||||{|}~~xqkpv}}yusyyyyyyyyyzzz{{||~{yvtr~~|z}xwvx}~~~~zwvwxxxxxxxxwwwwxwwwwxxyyzz{|{|{|}~~~~~~~}~~~~~|zwuuvvuwwwxwwvuvvuuuuvv}}}}~~|zywwvxyxvuwy}zvuttuvutuqrz~}}|}~}zy{}{z{}}}}}}}}~~}~~~}}~~}}||{{{{|{{{}~~~}~~~}}|{{}~~{y{}~}~}zz|~}}~~~~||||||||~}||}~~|z|~~~|zzzz}yz{zxz|}~~zwvvxxwxwxxxwwwwwwwwxxxyzz{{|||||~~~~~~~~}~~~~~}zwuvvvvvwwxxwvvuuuuvwxy~~}~~|{zyxxwxxxxx|zvuttuutsnlnw}}}|||}}zy|}|{|~~}~}~~~~~}}~~}}||{{z{||z{~~}~~~~~}}}~|yyz{z{~}~~~~}}|}}~~}|~||~~}~~yy}|~}|~zwvwxxxxxxxxxwxwxwxwxxyyz{{{}|}|}~~~~~}~~~~~}zwvvvvvvwxxxxwwttuvxz{}~||{||~~}}{zyz{|{z~|zvuttttsrjhltz|}}||{|}~~{{}~}~~~~~~~~}}~~}}||{{y{|{{{~~~}|}}|}~{zzz{zz}~~}}}{|||}~~~~|}~~}}~{xyz||||||||~~~}}|||~zy~~||~{xxyxxxyxxxxxxxxxxxxxxyzz{{|}}}}}~}~~~~~}{xvvwwvwxyyzyyxvvwxy|}~~|{z{{{~}}}~~~}~|{vussttsqkjnuyz{||{{{}~~{{~~~}}~~}}||{{yz{{{|~}}~~~~}{{|||{|~~~~}|{{zz{{||}}~~~~|{{|~~|z}}zxx~~}|||}~~|z~~}||yz{yyyyyyyyyyyyyyyyxyyz{{||~}}}~~}~~~~~}{xvvwwwxyzz{{zzyyyz{|}~|{{{{|~~~~~}|xwvuvvtsppsy{z{|{{{{|}~}{z}~~}}~~}}||{{yz{{{|~~~}{}~~~~~}|{zzy|||}}~~~}{{}~}|}}}~~~}z}~}}{~~}|~|z{|yyyyyyyyyyyyyyyyyyyz{{||~~~~~~}~~~~~~{xvwwwwyyz{||{{}||{{|}}yzz{}~}}~~~}{zyxyxwvutw|}{||{{z{|}~|zy|~~}~~~}}~~}}||{{xz{{{|~}}||||||||~~}}~~~}}}~||~~~~~~||{||}}~~~|}}~~}~}}}~~}{xvwz~~~}}|~}|yz|}~~}}|{zyyyyyzzzzz{|}~}}}}~~}}}}~~|||}~|yvuvwxxxz}~~|||yzz{||}}~~~~~~~~~~{{{zzyyyuwy{|}}}}}}}|||}}}}~~~~}||||{{{{{{|||||~}}~~~}~~~}}}}}}}}z|~~|{|~~~~}{}}|{{{|}~}~~~~}~~}{zz{||||}}|zxwx{~~}}}}}{|}~~~}||zzyyyy{{{{|}~~~~}~~~~}}}~~~||}}~{yvuvwxxxz|~}{zzyyzz{||}}}}}}}}}~~~|||{{zzzz{}~}}}}|||}}}}~~}}||||{{{{{{|||}}~~~|{~}|}~~~}|~~|{zz{{~~}}}~}}}~||{{{|}~z{{{zywvy|~}~~~~}}|||~~~}~}|{zzyzz|||||}~~~~~~~~~~~~||}~~{ywvwxxxxz}}|zyyzzz{||}}~}~}~}~}~~}}}}|||||}}}}|||}}}}~~}}}||||{{{{{||}}}}}~~~|~~~~~}~~}}|}~~}|||~~}}~|{zz~}}}|}}}zz{{|}}~{{zyxvuty{}}{yyz{{z{zzzz~~~~~}}|}}}}{{zzzz{{{{{|}~~~~~||}~~~{ywwxxxxxz}~}|{{||}}~~~~~~}}~~~~~~~~}}}}|||}}}}~~~||}||||{{{{{{|}~~~~~~~~}}}~~~~~~~}z|||~~}|zyx{{{||||{vwwxyyzz}|zxutrrwy{zyvwwxxxxxxyx{|||}}}~||{{}~~}|{{z{{|{{{|}~~~~|}}~~~~~{yxxyyxwxz~~~}}}}}}}|||}}}}~~~~~}||}||||{{{{{||~~~}}}}~~~~~}~}}~}}xyzz|}~~~|xw{}}}~}zyxwxyz{|{{zwxyzzzyy|{yvutsswy{{ywwxwxxxxyyyz{{||}}}||{{|~~~||{{{{}}}}~~~~~|}~~~~~{zxyyzxwx{~~~~}|}}}}|||}}}}~~}||}||||{{{z{|}~~~~~~~~~~~~{yz~}~|||{zzyzz||~~}yww}~~~~}~~~}|zyxwvwy{{{zyz{|}}{zyyxwuuvwxy{}}|{{|zz{{||}}{{|}}~||||}~~}||{||~~}~~~~~~~~~~~|}~~~~}{zyyzzywy|~~~~~~}}}}}|||}}}}~~}|}||||{{{z{|}~~}}~|~{||zz{}}}|}||||zxx}~~~~~}}}}~}}~}|{zyxxuvxz{zyxyz{{zywuvuuuvx{|{}~}}}~||}~}|||}~~}|||||}}|}}~~~~~~~~}}~~~|~~~~}}{zyyzzywz}~~~}}}}|||}}}}~~~}|}||||{{{z{|}~~|~}z{}}}}|{zyxyww{~~~}}~~~}}}}||zz{|}}~~ysrw|tvy{}|{zzzyyxxwwttuvwxxyz{|}}}}}}}|||}~~xyz{}|}}{z|~~~~~}||}~~~~~~~~~~~~}~|}~}ywvvvy|~~~~~}}}||~}||}}~~~~~~~}}}}~}|{zz{{|}~~~||}~}|yw{~{{|~}}|{{vx{~~~~~~~~~~}}}}}}}}|||{{{zz|zxy~}~~~~~~~|wssvytuxz{{zzzzyyxxwwtuuvwxyyxyz||}||~}}|||||{|||}}~~|}}{z{~~}}~~}}}}}~~~~~~~~~~~}~}}~|yvvvvx|~}}}||~}||}}~~~~~~}}}}}}|{z{{{|}~|{|~|{xvx}|z{|||{{zyyy~~~~~~~~~~~~~~}}}}}}}}|||{{{zz|zxz~~~~~~~|yvttuvstvxyyyxzyyyxwwwtuuvwxyyyy{{||||}}||{{{z~~~}}||}}}{zz~~~~}}}|}}}}}}}}~~~~~~~~~~|}~~~{xvuvux{~~~~~~}}}||~}||}}~~~~~~}}}}}||{{{{||}~~~~~~~~~}ywyz}{zwtuy{z|}}}}}||{{~}}~~~}}}}|}}}}||||}}}}~}}}}}}}}}}}|||{{{zz|zyz~~~~}~~~|xwvwutsstuvvvvxxxwwvvvtuuvwxyyz{{|{{zzyzz|||||~~~~~~~~}{yz|~~}}|||||}}~~~}}~~~~~~~~~{|~~~~~ywuuuuwz~~~~~~~}}}||~}||}}~~~~~~}}}}||{{z{{||}~~~~~~~~~~~~{|{ywx|}{{yutv}~~}~~~||{{~~}}}}||}}}}}}}}}}}}}}}}}}}}}}}}|||{{{zz{zy|~}||}}}}}~}zyzyvsrrrrssttvvvuuttssttuvwxxzzzyywvuuvy{~~||}~zxx{~~~~}}|||||~~}|}}~}~~~~~~~~~{|}~~~xvuuvuwz~~~}}}||~}||}}~~~~~~}}}}|{{z{{|||}~~~~~~~~~~}~}{|}|}{wuv~~~~~~~~~~~~}~}}|||||||}}}}}}}}}}}|}|}}}}}}}}}}|||{{{zz{zz}~}~}||~}|||}}{{{zvsrqpooprssssrrqqqrrstuuvwwwwwutrrrux|yz|}{wwy|~~}}||{|~~|{|||}}~}~~~~}~~~~~|||}~}|xutvvvwz}~~~}}}||~}||}}~~~~~~}}}}|{{{{||}|}~~~~~~}~~}|~~zwv}}~~~~}|~~~~}}}}|}|}||||}}}}}}}}}}}}}}}}}}}}|||{{{zz{z{}}~~}}}|{|}|||{zxurrpommnpqqppoonnnppqrstuuuuvvvutssuy}yz|~zwvx{~~~~~~~~~~~}z|~|z{{||||}}~~}~~~~~~~~}|}~~|zwuuvwvxz~~~~~~~~}}}||~}||}}~~~~~~}}}}{{{z{|}}}}~~~~}}}~~~~~~}}}}~{|{wv~~||}~~~}}~~}}}}~~~~~~~~~~~~~~||||}}}}}}}}}}}}}}}}}}}}|||{{{zzzz|}|~~|{|~z{{{yvsqqpnllmopoonnmmlloopqrsstuuvxxxxwtw{z{}~zvvxz~~~~~~~~~~~z||z{{{{||||~~~~}~~~~~~~}|}~~{ywuuwwwx{~~~~~~~~~~~~~~~~~~~~~}}}||~}||}}~~~~~~}}}}{{zz{|}~}}~~~~~}||}~~~}~~}|uy~}}~|zz~{wu}}{{|}}~~}~|{{|~~}}~}}|||}}}}}|}}}}}}}}}}}}~~~}}}}}}}}}}}}}}}}|||{{{zzzz|}{}}{|~|zwwvtpmlmpqqoljkoollnolllmnopqquuttssrrrtw{}~~~yyz|~}}|wtx}~}||}}~~}}|~~}}||}}}}}}}}~~~~~~~~}}}}~|||{zxwvvvvuvwxx~~~~~~~~~~~~~}}}}~~~}}}~~~~~~~~~}|{{zzzzz{}~|}~}}~~~~~|z}~}~~}~~|ww{{wvz|~}~||||||||yz{{z{}~|~}}~~~}}~}}}}}|||}}}}}~~~~~~~~~~~~}}}}~}}|||{{{xz~~zxwwyyvtqrtutrommopmmoomoopqrsttttssrrqqqsvyz{zzxwwy{|zy}}|{wux}~}||}}~~~}}~~~}}}|}}}}}}}}}}}}}}}}}}}}~|||{zxwvvvvvvwwx~~~~~~~~~~~~~~}}}}~~~}}}~~~~~~~~~}||{z{zzz{}~|}~}}~~~~~|{|}~}urwzywxz}||||||||y{{{z{}~~}~~}}}~~~~~~}}}}}}}}~~~~~~~~~~~~~}}}}~~}}||{{{{x{~~~|yxyxuswxyyxvsqnoonnoonppqrstuurrstuvwwrsvwyyxw|{{|~~|z|{{zxux}~~}}}~~~}}}~~~~}}}}}}}}}}}}||||||||}}}}~}||zyxwvvvvuvvxx|}~~~~~~~~~~~~~~}}}}~~~}}~~~~~~~~~~~~~}}|{{{{{z|}|}~}}~~~~~}~}}~~vqsyzxw~}zxy~~||||||||z{|{{{~~~~~}}}}~~~~~}}}}}}}}}~~~~~~~~~~~~}}}}~~}}|||{{{y{}}~|yvuvwut{{|{zwutnnmmnonmnooqqsstqqrrrrssstvwxyyy{zz|~~}{zyy{ywy}~~}}}~~~~}}~~~~}}}}~~~~~~~~}}}}}}}}}}}}~~}{zxwwwvvuvvwwx|}~~~~~~~~~~~}}}}~~~~~~~~~~~~~~~~~}}||{{{{{{|~|}~}}~~~~~~~yxyzyy~zwwuv~~||||||||z{||{|~~~~}}}~}||}}}}}}|||}}}}}}}}}}}}}}}}~}}}}}}~}}||{{{{y{~}~|}~~nllnswzz{{{zyxvunlkmnnmlnnooqqrsrrqqqpppqrtvwyyzxxx{~~|zxyz{y{~~~~~~}}~~~~}}}}}~~~~~~~~~~~~}}}}~~|yxvwwvvvuvvxx|}~~~~~~~~}}}}~~~~~~~~~~~~~~}}}}||||||{}~|}~}}~~~|z|~zwwtrx}||||||||z{}|||~~~}}}~}||}}}}}}|||}}}}}}}}}}}}}}}}}}}}}}}}}|}||{{{y|}}~~|}~~~}}}}}}mjhjnsuvzzyyxxxxplkmoomloopqrsttsrrrrrqqpqrtuwxyvvwy}~}|yxx||||~}~~}~~~~}}}}~~~~~~~~~~~~}}}}~|ywvvwvvuvvwwx}~~~~~}}}}~~~~~~~~~~}}}}|}|}||||}|}~}}~~~|yzwtw~}|||||||||{|}}|}}~~~~~~}}}}~~~~~}}}~~}}}}}}}}}}}}}}|}}}}}}|}}||{{{{z|}|~}}~}|{{zzz|~~sojikmooyyxxyzz{rmkorpnnppqrstuuutsrponmrrstuvwxsssuxyxvywx|~}~}}}~~~~~|}}}~~~~~~~~~~~}}}}~|xvvvwvvvuvwxx}~~~~~}}}}~~~~~~~~~}}}}}}}}}|||}|}~}}~~~}{||{z{~}|||||||||{|}}|}~|}}~~}~~}}}~~~~~~}}}~~~}}}}}}}}}}}}}||}~~}||}}|||{{{z}~}}~~}}|yyzz|~~rnihknpqyxxyz{}~uomqtrppppqrsttuuuttsssrvvuvvwwxyxxy{{ywywx}}}}~~~~||}}~~~~~~~~~~}}}}~|xvuvwvvvvvwxx|~~~~~~~~~}}}}~~~~~~~~|||}}}}}}}|}}|}~}}~~~~}|}~{~||||||||||{|}}|}}~z{|}~}}~~~}}~}}}}}|||~~~}}}}}}}}}}}}}||}~~}||}}||{{{{z}~}~zzzz{|~~wrljmrvx}}}|{zzzutsrrstuqqrsuuvwxwuttuvwvvwxyz{{{{{||}}}xyz|~~~~~~~~}~~}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}||{|}~}xuux{{wuvxxxy~~~~~~}~~}~~~}||||}~~~}}}~~~~~}|||||||}}~~~~~{zzz|}|{~|zy||||||||||||~~|||}~~}~}~}~}}}}}}}}}||}}}~~~}}}}}}}}||||||||{{{zyyyxw|||}~~~}|{zyx~yutw{}~}||{{zzyxxxxwwvvwwxyyzz{yxwvvvxyzzzz{{{{yzzz{{{|yyz|}~~~~~~~~}~~}~~~~~}}}}~~~~~~~~}}}}}}}}}||}}}yuux{{wuvwwwx}~~~~~~~|||||}~~~~~~~~~~~~~~~}|||||||}~~~~~~~|x|{zzzzyy~|{z|||||||||||}}~|||}~}}~}~}~}~}}}}}}}}}}}|||||}~}~}~}~}}}}}}}}||{{zzyyx}}|~~}|||~{xx{}~}~}}}|||{}~}{zyyyyyyyyyyxxxxyz|{{{{zzzyyyzz{{{||||}}~~~~~~~~~~}~~~~~~~}}}~~~~~~~~~}}}~}yvvx{{xvvwvvx}~~~~~~~}}|||||}~~~~}}~~~~~~~~}}||{||||}}~~~~~~~}}~{w}|{{{z~|{{||||||||||||~~||}~~|~}~}~}~}}}}}}}}}~~~}}||}}}}}}}}~~~~~~~~|||||{{{z~~}|~}{yz|}|z~~~~~~}}~|xxxxwwwwyyyyyzz{zzzzzzzz{{{||}}~~~~~~~~}}~~~~~~}}~~~~~~~~~~~~zwwx{{xvvvuvx}~~}}~~~~~~}|||||}~~}}~}}~~~~~~}}||{||||||~~~}~}}~}~~~}~}xx{}zw|}~}{{{|x}}}~}|{|||||||||||}}||}~~{}~}~}~}~}}}}}}}}~~~~~}}}|}|}|}||~~~~~~~~}}|}|}||{~~~~~|{{}~}{zzzzzzzzz||zzyzyyxxxyyz{{|||{{|}}~~~~~~}~~~~~~~~~~~~~~~~~~|xwyzzwvvvtvx}~~~~~~~~~}|}~~~~~||{|||}~}~~~~~~~~~}||{|{||||}}~}}~}}}~}~~|xuuxyxvxz|{yxz}}~~~}}||{||||||||||||~|}}~~{~}~}~}~}}}}}}}}}|||}}}}~}}}}}}}}~~~~~~~~}}}}}}}}}~~~}|}~|xutvxwvsssttuuutuwzzzyxzzzzzzzz{|}~~~}}}~~~~~~~~~~~~~~~~~}yxyzxvvvutvy}~~~~~~~~~~~~~~~~~}|{||||}~~~~~~}}||{{{||||||~~}|}~~|}}}~}~~~}}~~~~~~}~~~}}}|||{{{{{{zz|||||||||||}}~}~}}~}}~}~}~}~}}}}}}}}~~~~~~~~~~~|||||||||||}}}}~~~~~}~~~~yuomoqssqrrssttustuwy{|}||}}~~~~~}~~~~~~~~~~~~~~}}}~}zxyzvuuvutw{}~~~~~~~}}{{{|||}~~~~~~~}}}||{{{{||||}}}||~~|||}}~~~~~~~~}}}||{{{zzz{{{zzyyyyxyyyyyy||||||||||||~}~~}}~~}~}~}~}}}}}}}}}}}}}}}}}{{{{{{{{{{||}}}}~}~}}~~xqnpsvwtuuvwwxxvwwy|~~~}~~~}}}}}}}}~}||}}~~{yyzutuvutw|~~~~~~}}~~~~~~~~~~~}|{{{|}|}~~}~~}~~~~~}}}||{{{{||||}~~~}{|~}|||}}}~~~~}|vvz~~}}||{{{{zzzyyxxxwwwvvzzyyxxwwwwwxxxxy||||||||||||~~{~}~}|}~}~}~}~}}}}}}}}||{{zzzzzzzzzzzzzzzzzzzzz{{||}}}~}}}~}{yy{||{{wuwzwy{|~}}~~~~~~~~}~}}~~}||||}}~~~~~~}~{yyzxwvuuwz}~~~}}~~}|{{|||{}}~~~~~~~~~~}||{{{{{{{{{{{{|}~~||~~|||}~~~~~}~{ywvvyy{~zv{zyxxy{|yyyyyyyyyyyyyyyy{{{zzzyyyyyxwvvv{||}}||{{|zy|~~~~~~~~~~}zzzyzz{{yyyyzyxwxyyzzzyyyyzzz{{{z{|}}}||~~|{{}}}yvwxxz{||~~~~~~~~~~}}~~}~}}}}}~~~}~~~|z{|yxvuux|~~}|z{|}||}}}~~~~}||{{{{{{{{{{}}~~||~~||}~~{{}}z|~|ywvutwwx{}~}||{zyxyz{||||||||zzzzzzzzxwwwvvuuuuuvvwww{||}|||{{|{z}}||||{{{~}zyyyyzz{yxxyyzyxyyyzzzzz{{{{{{{|{{|}}}}|~yz{}|xvuyz{{{z|}~~~~~~~}}}~~}~~~~~~}}}~~~}~|}yxuuvz~~|{zz|}}}||}}~~~~}}|{{{{{{{{{{~~~||~~}}~~zy{}|y{}|{ywutswwwwxy||{zxxxxyxxxxxxxxwvwvwvwwvvvuutttuuvwwxxy{|||}||{|}|{~~}|yyyxyyzzyxxxzzzyyyyzz{{{||||||||{|}~~~}}~xwx{~~zurwyzzyyyz~~~~~~~~~}}~~~}}}~~~~zxuux}~~|{z{|}}}{||}~~~~~~~}||{{{{{{{{{{}~~~||}~}~~|z{~~{|||{zxvtsxyxwuux{zzyxwwvvttttttttvvvvvvvvyyyxxwwwzzzzyyyy{||}|||{}~}{~}~~}}{zyxxxxyyzzyxyz{zzyyzz{{||}}||{{{{||}~~~~~~~|ywx{|uqtvxyyxyyz~~~~~~~~~~~~~~~~~~}~zxuvy~~~~|{zz|}}}{||}~~~~~~}}|{{{{{{{{{{{||}~~||~~}~~}yy}~||~yzzywusruwxxvuwxzzzzzxxwyyyyyyyy{{{{{{{{{{zzzyyyzzzzyyyy{|||}||{}~}{~||}~~~~~||{xxxxxxyy|{zy{{{{zzzzz{|}|{{{{zzz||}~~~~~~~}~~~~~~~~~zy|}vrrtwyxxxyxz}~~~~~~~}~~~~zwuw{}||~~}|z{{|||||}}~~~~~~}||{{{{{{{{{{{{|}~~||~~}}~~{wwz|{z{wwxxwtrppsvxxxxx{||}}}{{||{|{|{|z{z{z{zzyyyxxwwwstuvxyzz{||}|||{|}|{~~~}|xxxxxyyz}|z{{}}}{{zzz{||zzzzzzzz{|}~~~}}~|{}}xsqtwyyxxxuuwz~~~~~~~~~~~~}~}ywvx}}|{{}~~~~}{{{|{z}}}~~~~~~~}}|{{{{{{{{{{||}~~||~~||}~~~}~~|xv{}|{{yz{{zxusrstwyyxwwxz|}||{yyyyyyyywwwwwwwwxxxwwvvvrstuwxyz{|||}||{{|{z}}~}|{yxxxxyyz|{z{}|{zyyz{{zzzzzzzz{{|}}}}|~}{{}~}~~}yuruxyxwwwrrty~}}~~~~~~}~}~~~~}}ywvy~}{zz{}~~~|{{{zz}}~~~~~~}||{{{{{{{{{{~~~||~~|||}~~~}|}~~~zy}}~}~}zxvuuvxyvtrtvyzzyyyyyyyyyyxxxxxxxxyyxxwwwvwwwwxxxx{||}}||{{|zy|}~|{yxyyxxyyzz{zz{~}|zyyyz{yzzzz{{{z{|}}}||~~{z{|~}{||{zyxwutuvy}}~~~~~}}}}~~~~~~}{{zvw~}zyz~~~~}}}|||||z{|}~}~~~}}}}}}}}{{{{{{{{|}~~{z|~~~~}||~~|~~~~~~~~{ywwy{||{zyxxyyy{zzzzyyyxvtuwyyxwxyyyyxwwwwwwwwwwwwwwwwwwvvvwxyy{}~|{{ywwuttvz~~~~}~~~~}|yyxxwwwxz{{{}zzzzyy{{{zzzzzz{||}}|||{~~~~{zyz{}}}~~~~|{zyvvvwz}}~~~~~~~~~~}||{wx|zz{~~~}}}}||||z{|}~~~~~~}}}}}}}}{{{{{{{{|}~~{{|~~~}|}~}|~~~~~~~~}{xxy{||zyxxwxxxzzzzzzzzyxvwyzzyvwwxxxwvwwwwwwwwwwwwwwwwvvvvwxyy{}~|{{ywwvttvz~~~}~}zyxwwxyyz{{|~|{yxyyyx{{{{{{{{z{{}}}||~||~}~}~|yxz{}}}|{xxxxz|~~~~~~~~~~~}}}}}||{xy|z{}~~~~}}||{z{|}~~~~}}}}}}}}||||||||||~}{{}~~}~}}}~~~}~|yxzz{{zzyxwwxxyyzz{{||{yxxz{{zwwxxxwwwwwwwwwwwwwwwwwwwwvvvwxyy{}~|{{ywwuttvz~~|{z|zyxxy{|y{{|~~{zyyyzyyzz{{{{||z{||}||||}|~|~~~~~~~~~~~{yz}~~}}||}~~~~}|{zzz{|~~~~~~~~~}}}}||}}|||zz~|{}~~||{{z{|}~~~}~~}}}}}}}}|||||||||}~}{|}~||}|~~~|yxxyyy{{zxxxxxxxyz{|}~{zyyyz{{xxxyxyxxwwwwwwwwwwwwwwwvvvvwwxyy{}~|{{ywwvttvz~~~~}{yxzywvvxz{yz{|}~{{zzz{{{{yzzzz{{{z{{}|}|||~}}~}}}}}}}}}~~~~~~~}}{xz|~}}{zyxxzz||~}}}|||{||}}~~~~~~~~~}}}}}||||~}}|{{}~~~~~||~~}||{z{|}~~~~~}}}}}}}}||||||||||~~}|}~{{{}}}zxvwxyx{zywwvvwwxxz{|}~zzzyxyzzxxxxxxxxwwwwwwwwwwwwvvvvwvvvwxyy{}~|{{ywwuttvz~~~~|{zyxwwxz{yz{{}~}zyyzzzzzzyyyyzzzzz{||}||||~}~~~}}}}}}}}~~~~~~}}}~yxy|~~}|{zyyz{|}~~~~}}}}}}}}~~~~~~}~}}|}|||~~}~}|}}~~~|}~~}}|{{z{|}~~~~~}}}}}}}}|||||||||}~}}|~}{z|}~~|zwwwyyyzywvuuuuwwxyz{|}z{{zxxy{xxwwwwwxwwwwwwwwwwwwvvvvvvvwwxyy{}~|{{ywwvttvz~~}|zzyyxxyyyzzz{}|zwxyyyxxyzzyzyzyzz{{}}}||{~~~~}}~~~~~~~~~~~~~~~}zy{}}}}~~}}}~~~~~~~~~}}}}|{~~~~~~}}~~~|{{z{|}~~}}}}}}}}}}}}}}}}}}||~}|}~}z|}~~|zxwy{||{{yxwvvvvwwxyz{{||}{yyz|xwwvwwxxwwwwwwwwwwwwvvuuwvvvwxyy{}~|{{ywwuttvz~~}|zz{zzxwvzzzyz}}{z|}}|{{{|{{{zzzzz{||}|||z}}~~}~~~}{{}}}}~~~~}~~~~~~~~~~}}}}{~~~~~~~}}}||{{|||}}}}}|{|~~|{{z{|}~~}|}}}}}}}}}}}}}}}}|}~}||~}z}~}~~}{yy{}~~~|{zyyywwwxxyyz}~~}zz{}yyxwwxyywwwwwwwwwwwvvvuuvvvvwxyy{}~|{{ywwuttvz~~}|zzyxz}~}}}||{{zzz{||}}||x{}~~~~~~}}|}}~~~~~~~~~~}}~~~~}~~~~~~~~~xxy|~}zwxyz||}}}~|{{}}}}}}}|||}~~~}||}}}~~~~}}}||||||||{|||}}}~|{~~}~~~~~~~~~~~~~}}}}||}|}||||||||||||||}|{{zzxxxyyxwxxxwwwwwwvvvwwwvvvvvwwxxz|}|}}zvutstvy}}{z{~~}}||{z{{||{zz{{|~~~~}|}}~~}~}~~~~~}}}}~~~~~~~~~~~~~~}~~~~~~~{zz|}}zxyz{|}}||}~~~{zyz}~~~~}}}~~~~~~~}||||||||{|||}}}~|{~~}~~~~~~~~~~~~~}}}}|||||||||||||||||||||{zzyyxxxyxwxxxwwwwwwvvvwwwvvvvvvwxxz||||}zvutstvy}~}}~~}|{{{}~~~}|||}}~~~~~~~~~~~~~~}}}}~~~~~~~}~~~~~~~~~~}||}}{z{||}}}||z{{{zz|~~~~~}}}~~~~~~~||}|}|}|{{||}}}}|{~~}~~~~~~~~~~~~}}}|{{{{{{{{||||||||{||||{zzyxxxxxxwxxxwwwwwwvvvwwwvvvvvwwxxz|}|}}zvutttvz}}|zzz{|~~}}|{{{|}~~~~~}}}}~~~}~~~~~~~~}}}||}}~~~}||yz{||~~~~~~}}}}}}}}{||}|}}~|{~~}~~~~~~~~~}~~}}||z{z{z{z{||||||||{{|||{zyxxxxxxwwxxxwwwwwwvvvwwvvvvvvvwxxz||||}zvuuttvz~~{}{zyyyyz|}~~}}{{z{{|~~~}}}}}}}~~}}~~~~~}}}~~~~~}|}~}~~~~~~~~}}}}}}}}{{||}}}}|{~~}~}~~}||{{z{z{z{z||||||||{{{|{zyyxxxxxwwwxxxwwwwwwvvvwwwvvvvvwwxxz|}|}}zvvutuwz~~~|z{|}~}{zyyyyz{}~~~}|{{z{{|}~~}}}}}}}}~~~~~~~|}~~~~~||}~}~~~~~~~~~~~}~}~}~{||||}}~|{~~}~~~~}|{{{{{{{{{{||||||||z{{{{zyywxxxwwwwxxxwwwwwwvvvwwwvvvvvvwxxz||||}zvvuuuw{~~||}~~~~~{|}}|yyyz{}~~}|{{{{{|}~~~~}}}}}}}~~~~~~~~~~|}~~~~}~~~~}{{{}~~~}|}}}}~~~~~~~~~~~~~~{|||}}}}|{~~}~~~~}{{z||||||||||||||||zz{{{zyxwxyxwvwwxxxwwwwwwvvvwwwvvvvvwwxxz|}|}}zvvvuuw{~~~~}|z{{{||}}~~}|yyz{}~~||{{|z{}~~~~~~~}}}}}}}~~~~~~}}~~~~|}~~~{~~}~|~~}}}}}}}}||~~~|{z||}}}~~~~~~~~~~~~~{|||}}}~|{~~}~~}}~}|zz|||}|}||||||||||zz{{{zyxwxxxwvwxxxxwwwwwwvvvwwwvvvvvvwxxz|}|}}zvwvuux{~~}}~|zxxz{{||{zzyyz|~~~}||||yz|~~~}~~~}}}~}{{z{{{}~~~~~~~~~}}~~~~~~~~~~~~}}~~~~~~}|||~~~|||~~}~~~|zy{|}~}~~~~~}|}}}}~~~~~~~}}}~~~~~~}}|}}}}}}}}}}{{|{|{|{yyxxxxxxyxxwwwwwxxwwwwwwvwwxxwvuwvuvwwxzz}~}}|zxuttvy}}~~~~}|{zzz{~||~~~}|{~}||}~~~~}~~~~~}|{{{|}~~~~~~~~~~~~~~~~~~~~~~~~~~}~~~}|}}|}|}}}}~~~|zz{|}~~~~~}}}}}}~~~~~~}}}~~~~~~~}|||}|}|}|}|}{{{{{{{{yyxxxxxxxxwwwwwwxxwwvwwwvwwxwwvuwvuwwwxzz}~}}}{xvvuwz~~}|zzz{~}}~~}||||{{|~~~~~}}}~}}|||}}~~~~~~~~~~~~~~~~~~~~~~~~}{~}|{{{}}|}}~~|{z||~~~~}~~~}}~}}}}~~~}}}~~~~}}~~~~|||||||||||||{{{{{{{{yyxxxxxxyxxwwwwwxxwwwwwwwwwwwwvvwvvwwwwyz|~}}|{xxwwy|}|zzz{}~|~~}}}|{{{|}}}||}}}}}~~~~~}}~~~~~~~~~~}}~~~~~~~~}~~~|{~}||{||}{||}~~}{z|}}}|~~~~~}}}}}}~~~}}}~~~~}|||~~~~}|{{{{{{{{{{{{zzzzzzzzyyxxwxxxxxwwwwwxxxwwvwwxwwwwwwvvwvvwxwwxz|}}|}{yzyy{~|{zzzz}~}~~~~~~}}}|}|}}~~~}}}~~~~}|~~~~~~}}}~~~~~}~~~}|{{}}}{}}~~~}}|{{||~~}|{|}~~~}~~~~~~~~}~~~~~~}}}~~~~}||||}~~~}|{zz{{zz{z{zzzyyzyzyzyyyyxxxxxyxxwwwwwxwwvwwxxwwwwvvvvwvvxxwwx{|}||||z{{{}}{yzzzz|}~}~~~}}}}}}~~~~||~}}}~~~~~~~~~~}}}}~~~~~~~~|{z{|}|{|}~~}{||}}~|{}}~~~~~~~~~~~~~~~}}}~~~~}}~~|}}~}|{zzzzzzyzyzyzyyyyyyyyyyxxwxxxxxwwwwwwwwwwwwxxxwwvvvvwwvwxxwvw{|}||}|z|||~~~}zy{zzz|~~~}~~~}}|||}~~~}~~~~}}|}}~~~}}}~~~~~~~~~~~~}}}||}}}~~|{{}}|{{{|}~~||}}~~|{}}~~~~~~~~~}}}~~~~~~}|{zyyyzyyyyyyyyxxxxxxxxyyxxxxxxyxxwwwwwwwwvwwxxxwwvvvwwwvwxxwvw{|}{|||{}|}~}|{{zyz{~~~~~|~}|{{z{{}}~|{z~~~~~}||}}}}~~~~~~}|}}}~~~~~}}||||}}~}}}}{z{zz{|}||}~~|{}~~~~~~~~}}}}~~~~~~~}}}~~~}|{zyyyyyyyyyyyyxxxxxxxxyyxxxxxxxxxwwwwwwwwwwwxxxxwvvvwwwvwyxwvw{||{{}|{}}}}~~~~}|~}{zyz{~~}~~{~}|{zzxy{|{zyx~~~~~~~~}~~}}}~~~}}~~}||||{{|}}}}|zz{}}||}~|{|}~~~}}}}~~~~}}}}|{zzyy||||}~~~~}}~~}~~~~~~}}~~~~|}~{{{zzzyyzzzyyyyyyyyyyyyywwxxxxyyxxxxxxxxxxxxwwwwwwwvvvuuwuvxyxxz{|}}|{{{vwy|~~||}}}}{ywx{}~~|~~~~~}}~}|{zz{{|}~~}}~~~~~~~~~}~~}}}~~~|}~~~~|zyyxxzz{||||{{{{}~|{{{|}}~}}|{~~}}{{{{~~}||{{{||||}~~~~~~~~}}}~~~~~~~}}~~~~~~~{{{zzzyyzzzyyyyyyyyyyyyyxxxxxxxxxxxxxxxxwwwxwxxxwwwwvvuuwuvxxxxzz|}}||||xxz~~~}~}|}{yxx{}~~|~~~~~}}~}||{{||}~~~~~~~~~}~~~}~~~~}}~~~~~~~~}{yxwxxyyz{|{{z|||}~|z{|}}}}}{{z~}}|||||~~}}||||||}~~~}}~~~~~~~~}}~~~~}|{~~{{{zzzyyzzzyyyyyyyyyyyyyxxxxxxxxxxxxxxxxwwwwxxxxwwwwvvvvwuvwxwxyz{}}}|}~{{}}~~}||{yxy{}}}|~~~~~}}~~}}|||~~~~~~}~~~}~~~}~~~~~~~~~~}{{z{yz{|||{{}||}~}}||}}}}|{{}~~~}}|||||}~~~~~~~~}}~~~~~~~||~~~~~~~~}|~{{{zzzyyzzzyyyyyyyyyyyyyyyxxxxwwxxxxxxxxwwwxwxxxwwwwvvvvwvuxxwwyy{|~}}~~~~}~~~~}~}}}~}||{zyz{}|||~~~~~}}~~~~~~}}~}~~~~~}~~~~~}~~~~~~~}|}}z{||}}}|}||}~~}z{|}~}~~~~~}||||||}~~~~~~}|~~~~~~~~~~~~~~~}|{~{{{zzzyyzzzyyyyyyyyyyyyyyyyxxwwwxxxxxxxxxxxwxwwwwwwwwwwvwuvwxvvxyz}}}|}~}}}}}|{|~~{{z||~~~}~|{{{zzz}}|z|~~~~~~~~}~~~~~}~~~~~~~}}||}~~~~~~||{|{||}}~}}}|{|~~~|}~}~~~}||||||}~~~~~~~~}}~~~~~~~~}|~{{{zzzyyzzzyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwvuwwvvwy{|}|||}~}|}~~|zz|~~~zxwy{}}~~~~|{{{zz{}}{y|~~~~}~~~~}}~~~~~~~~~~~~~~}}||}~~~~}~|{{{{z{||}||||{{|~~||~}~~}|||||}~~~~~~~~}}~~}~~~~}~~{{{zzzyyzzzyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxwxwwwwwwwwwwwwuvwwuuwy{||{z{{~~}}~}}~|{{}~~}||{}{yxxy{|~~~~|zz{zz|}}{x|~~~~}}~~~~~}~~}~~~~~~~~~~~~~~~~~~~}}|{{{yzz{|{{{{zz|~|}~~~~~~}||||}~~~~~~~~}}~~}}~~~~~~|~~~{{{zzzyyzzzyyyyyyyyyyyyywwwxxyyyxxxxxxxxwwwwxxxxwwwwwwwwwvuwwuuwy{||zzzz}}~~}|}~|{{}~}}|{{zzzzzzz{{|~}}~~|zz{z{|}}zx|~~~~}}~~~~}}~~~}~~~~~~~}~~~~~}}~}~|zyyyxxz{{zzyyyz|~~~~{||}~}|{{|}~~~}}~~~~~}~}}~~~~~|{zzyzz{yyyyyyyyxxxxyyyywxxxxxxxzyxwwwxxyyxwwwwxwwwwvuttvvvvwwwwz{||{{|}~|~}|}~~~~|zxxy{|}}}}}}|||~|{{{}~{yzzyz{~}}}||}}}~~~~~~~~~~~~~~~}~~~~~~|{zyywxyzzzyx{{|~~~~{||}~~|{{|}~~~}~~~~~~~~~~~}~~~~~}}~~~~~||zzyyyzyzyzyzyzyyyyyyyyyxxwwxxyyyxwwwxxxxwwwxxyvwwwvvuuvvvvvwwwz{||{{|}~~zz|~~~~~~~~}{z{}~~~~}}}}}}{{{{{||}|{|zzz{}~}}}}}~~~~~~~~~~}~}~~~}|{{{z{{|||||~~~~}~~~~~~~~}}|~||}}~|{{|}~~~~~~}}~~~~}~~}}}~~~~~}|{yyxxxzzzzzzzz{{{zyxxxzyxwwwyyyyxxwxxxwwwwxxyywwxwxwwvvvvvwwwwz{||{{|}~~|||~}||}~~|||}~~~~~~~}}}yz{{|||{}}}~{zzz|~~~~~~~~~~~~~}~~~}~~~~|}|}}~~~~~~~~}}}~}}}|}}}~}||}}~~~|{{|}~~~~~~~}~~~~~~~}{zxxxx{{{{{{{{}}|{zyxxzyxxwxxyxxxxxxxxwwwxwxxywxxyyyxxvvvwvwwwz{||{{|}~|{{}}||{|}}}{zz|}}}~~}}||}}}}}}}}}~{{zz{~~~~~~~~}}~~~~}}~~~|}~~~}~~~~~~~~~~~~~~~}}}||}~~~~{{|~~|yv}}~~~}|||~~~}~~}}}~~~~~~|zzxyx|||{|{||~}}{{yyxyyyyyxxxxxxxxxxxxxxxxwwwwwxxxxxxvvvvwwwwz{||{{|}~~}~~~}|{|}{{||}||{zyyz}~~~}}}~~~~|{{{{zz{~~~~~~~~~~~~}~~~~~~}}~~~|}~~~}~~~~~~~~~~~~~~~}{z{|}~}}}~~{xu}}~}||}~~}}~~~~}}~~~~~~~~}}~~~~~~}|zzzz|}|}|}||~~}|{{zzyzyzyyxwwwxxyxxxxxxyxxwvvvvwwwvvvvvwvwwwz{||{{|}~|z{|||zywvxyz{||}}z{|}}|{zzz{}~}}}~~~~}{zy{zzz|~~~~}}~~~~}}}~~~~~~~}}~~~~}~~~~~~~~|zz{}}~}|}~~~||}~~}}~~~~~~~~~}}~~~~~}}}~~~~~~}||||}}}}}}}}}}}}|}|||zzzyyxxxwwxyyyxxwxxxyxwwwwwwwvvuvvvvwwwwz{||{{|}|yxz{||{ywvwxz|}|{y||||||||||}~~}}|~}~~~|{zzzz{}~~~~||}}~~~}}}}~~~}}~~~~~}}~~~~~~~~~~}~|{||~~~~}~~~}}}~~}}~~~~~~~}}~~~~~~~}}}~~}}}}}}}}|||}}}}~{{yxwxxyvwxyyyxxvwxyyyxxyyyyxxwvvvvvwwwwz{||{{|}~{xvyz{}}|zyxxy{|~~~}}{{{||}}}~~}}||~~~~~}}|~}|{zyz{~~~~~~~~~~~}~}~~~~~~}~~~~~~~}~~~~|z{|~~~}}~~~~~~~~~~~}~~~~~}|}~~~~}}}}}~~~}}|}}|||}}}}|zzzzzzzz{||{yxxywwxxxyyywwwwwwwwwwxxxxwvxwwwvwwwxz||{zz{~~}zyz{{zyxxywxyz{|||}}}~~}}}~~~~}|{{{}~|zz~}|zzz{~~~~}~~~}}}~~~~~~~~~~~}{{|~~|zy~~~}}~~~~~~~~~~~~~}~~~~}|}~~~~}~~~|}}}}}||{{z{{{{{zz{zxwwxxxxxxyxyyxxxxwwwwxxxxxwwxwwwvwwwxz|}||||~~}zyyzzzyyzzyyz{|}}}}~~~~~~}}~~~~}|{||||}}|||{|~~~~~~~~~}|}}~~~~~~~~~~~~~~~~~~~|||~~~~~~|{~~~}~~~~~~~~~}}}~~~~~~}|}~~~~~~~~~~}}|{{{{{{{{yyzyxxxyyxyxxxxxzzyyxxwwxxxwwwwwwwwvwwwxyz|}}}}}}zzyz{{{{}~{||}~~~~}~~}|{~{{|~|{~}||}~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~~}}~}}}}||}}}~~~~~~~~~~~~~~}}}~~~~}|}~~~~~}||{{{{z{zzyyzzzzzzyyyyxxwwyyyxxxwwyxwwvwwxwwwvwwxxyz{}}}||}}zz{|}~~}~}||~}{~~}{{}|{{}~}~~~~~~~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~~~~}}~~}{{}}}||}}}~~}}|}}~~~~~~~~~~~~~~~~~}~~}|}~~~~~~~}~~}}|{z{zzyzyyyyyzzzzyyyxxxxwwwwwwwwwyxxvwwxxwwvvwwxxzz{||{zy{|{z}}}||}}~~||{{zz{}~}~~}~~}~~~~}}}}~~~~~}~~~~~~~~~~~~~~~~~}{{|}}}|}}}~}{zz{|~~~~~~~~~~~}~~~}}~}}}~~}|}~~~~~~~~~}}}~~~~~}}{{z{zzyyxxwxyyyxyyxyxxxxwwwwwwwwxxwwwwwxwvvvwwxx{{{|{{yxz}{{}~~~~}|{z}}|||zzzz|~~}}~~}~~~~~~}~~~~~~~~~~~}~~~~~~~~}||}}}|}}~~~}{{{|}~~~~~~~~~~~}}~~~~~~~}|}~~~~~~~~}}}~~}~~~~~}}|||{{zzzyxwxxyxwxxxxxxyyxxxxxwwwxxxxxwwwvvvvwwxy{{{|}|{yz|{{|}~~~~~}|{z}~}||}|{zz|~~~~~~~}}}~~~~~~}~~}~}}}}~~~~~~}~}~~~~~~}}}}|}}~||||{{zz~~~~~~~~~~}~~~~}|}~~}~~~~~~}}}~~~~}~}}}||{{zyxyyzywwwxxxyyyzyyyxxwwwwxxxxwvvvvvwwxy{{{|}~|{{|{{}~~~~}{z{~}}}}~}{{{}}}~~~}~}|}~~}~}}~~}}}~~~~~~~~~~~~~~~~~~}}}~~~}|{{{|~~~~~~~~~~~~~~~~~~~~||~~~~~~~~~~~~}}}}~~~~|||{{zzzzyxyyyyxyxyxyxyxxxxxxxxxwwwwwwwwwwwwwwxx{{|||{{{~}|{|~~}|{|}~}}||||{{}~~}~~}~~~}}~~~}}}}~~~~~~~~~~~~~~~~~~~~~~}}}~}|}}}~~~~~~~~~~~~~~}|~~}~~~~~~~~~~}}}}}|||{{zzzyxyyyyxxyxyxyxyxxxxxxxxwwwwwwwwwwwwwwxxz{||{|||}|{|~~}|{{||}|}}||||{{}~~~~~~}}}~~~}}}}|}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}||~~}}~~~}~~~~}}}}}|||{{{zzyxyyyyxyxyxyxyxyyxxxxwwwwwwwwwwwwwvwwxxyz||||}~~|{|~~~~{{{{||||{|~~}~}||||{{}}}~~~}}~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}|~~}}~~~~}~~}}||||{{zzzzzyxyyyyxxyxyxyxyyyyxxwwwwwwwwwwwwwwwwxxxyz{|{|~~}|}}~}~~~~z{{}}}}}|}~~~}|}~}|}||{{}~~~}~~~~~}~}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~||~~~~~~~}~~~~~~}}||{{{zzyyyzyxyyyyxyxyxyxyxyyyxxwwwwwwwwwwwwwwvwwxxyz||{|~}|}~}~~~~||}~~~~~~}}~}|}||{{}~~~~~~}}~~~~~}~}~}}~~~~~~~~~~}}}}~~~~~~~~~~~~~~~~~~~~~}|~~~~~~~~~~~~~~~}}}}{{{{zzyyzyxyyyyxxyxyxyxyyyxxxxwwwwwwwwwwwwwwwwxxy{||z{~}|}~~~~~~}||||{{}~}}}~~}}}}~~~~~~~~~~~~~}}~~~~~~~~~~}~~~~~~~~~~~~~~~}~~~~~}|{{||}~~~~~~~~||}}~~~~~~~~~~~}}}}|||{{zzzzyxyyyyxyxyxyxyxxxxxxxxxwwwwwwwwwwwwwwxxz|}|z{~}|}~~~}||||{{}~~~}}|}~}~~~~~~~}~~~}}}~~~~~~~}}~~~~~~~~~~|}}~~~~~~|zyzz{|~~~~~~~~~~~~~~~~~~~||{|}~~~~~~}}}}~~~~}}}||{{{zyxyyyyxxyxyxyxyxxxxxxxxwwwwwwwwwwwwwwxx{|}|zz~~|~~~~}}}}}||||{{}~~}|||~~}}~~~~~~~~~~~}}~~||||||||~~~}}}}xxwxz|~~|}}~~~~~~~~~~~}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}}||{{{yyyyyyyyyyyyyyyyxxxxxxxxvvvvvvvvxxxxxxxxz{{|}~~~~~~~~~~~~~~~~~~~|z||{|}}}~~~~~~~~~}}||}}~}}}}}}}}~~}}}|||{{z{|~~~}}~~~~||||||||~~~}}}}}}}}|||{{{zzyyyyyyyyyyyyyyyywxwxwxwxxxxxxxxxwwwwwwww{||}~~|z}}|}}~~~~~~~~~~~~~}~~~~}}}~~~~~~~~~~~ \ No newline at end of file diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/dropevent.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/dropevent.py new file mode 100644 index 0000000..e812558 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/dropevent.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +""" pygame.examples.dropfile + +Drag and drop an image on here. + +Uses these events: + +* DROPBEGIN +* DROPCOMPLETE +* DROPTEXT +* DROPFILE +""" +import pygame as pg + +if pg.get_sdl_version() < (2, 0, 0): + raise Exception("This example requires SDL2.") + +pg.init() + + +def main(): + + Running = True + surf = pg.display.set_mode((640, 480)) + font = pg.font.SysFont("Arial", 24) + clock = pg.time.Clock() + + spr_file_text = font.render("Feed me some file or image!", 1, (255, 255, 255)) + spr_file_text_rect = spr_file_text.get_rect() + spr_file_text_rect.center = surf.get_rect().center + + spr_file_image = None + spr_file_image_rect = None + + while Running: + for ev in pg.event.get(): + if ev.type == pg.QUIT: + Running = False + elif ev.type == pg.DROPBEGIN: + print(ev) + print("File drop begin!") + elif ev.type == pg.DROPCOMPLETE: + print(ev) + print("File drop complete!") + elif ev.type == pg.DROPTEXT: + print(ev) + spr_file_text = font.render(ev.text, 1, (255, 255, 255)) + spr_file_text_rect = spr_file_text.get_rect() + spr_file_text_rect.center = surf.get_rect().center + elif ev.type == pg.DROPFILE: + print(ev) + spr_file_text = font.render(ev.file, 1, (255, 255, 255)) + spr_file_text_rect = spr_file_text.get_rect() + spr_file_text_rect.center = surf.get_rect().center + + # Try to open the file if it's an image + filetype = ev.file[-3:] + if filetype in ["png", "bmp", "jpg"]: + spr_file_image = pg.image.load(ev.file).convert() + spr_file_image.set_alpha(127) + spr_file_image_rect = spr_file_image.get_rect() + spr_file_image_rect.center = surf.get_rect().center + + surf.fill((0, 0, 0)) + surf.blit(spr_file_text, spr_file_text_rect) + if spr_file_image: + surf.blit(spr_file_image, spr_file_image_rect) + + pg.display.flip() + clock.tick(30) + + pg.quit() + + +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/eventlist.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/eventlist.py new file mode 100644 index 0000000..b14d523 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/eventlist.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python +""" pygame.examples.eventlist + +Learn about pygame events and input. + +At the top of the screen are the state of several device values, +and a scrolling list of events are displayed on the bottom. + +""" + +usage = """ +Mouse Controls +============== + +- 1st button on mouse (left click) to toggle events 'grabed'. +- 3rd button on mouse (right click) to toggle mouse visible. +- The window can be resized. +- Mouse the mouse around to see mouse events. +- If events grabbed and mouse invisible show virtual mouse coords. + + +Keyboard Joystick Controls +========================== + +- press keys up an down to see events. +- you can see joystick events if any are plugged in. +""" + +import pygame as pg + + +img_on_off = [] +font = None +last_key = None + +# these are a running counter of mouse.get_rel() calls. +virtual_x = 0 +virtual_y = 0 + + +def showtext(win, pos, text, color, bgcolor): + textimg = font.render(text, 1, color, bgcolor) + win.blit(textimg, pos) + return pos[0] + textimg.get_width() + 5, pos[1] + + +def drawstatus(win): + global virtual_x, virtual_y + bgcolor = 50, 50, 50 + win.fill(bgcolor, (0, 0, 640, 120)) + win.blit(font.render("Status Area", 1, (155, 155, 155), bgcolor), (2, 2)) + + pos = showtext(win, (10, 30), "Mouse Focus", (255, 255, 255), bgcolor) + win.blit(img_on_off[pg.mouse.get_focused()], pos) + + pos = showtext( + win, (pos[0] + 50, pos[1]), "Mouse visible", (255, 255, 255), bgcolor + ) + win.blit(img_on_off[pg.mouse.get_visible()], pos) + + pos = showtext(win, (330, 30), "Keyboard Focus", (255, 255, 255), bgcolor) + win.blit(img_on_off[pg.key.get_focused()], pos) + + pos = showtext(win, (10, 60), "Mouse Position(rel)", (255, 255, 255), bgcolor) + rel = pg.mouse.get_rel() + virtual_x += rel[0] + virtual_y += rel[1] + + mouse_data = tuple(list(pg.mouse.get_pos()) + list(rel)) + p = "%s, %s (%s, %s)" % mouse_data + showtext(win, pos, p, bgcolor, (255, 255, 55)) + + pos = showtext(win, (330, 60), "Last Keypress", (255, 255, 255), bgcolor) + if last_key: + p = "%d, %s" % (last_key, pg.key.name(last_key)) + else: + p = "None" + showtext(win, pos, p, bgcolor, (255, 255, 55)) + + pos = showtext(win, (10, 90), "Input Grabbed", (255, 255, 255), bgcolor) + win.blit(img_on_off[pg.event.get_grab()], pos) + + is_virtual_mouse = pg.event.get_grab() and not pg.mouse.get_visible() + pos = showtext(win, (330, 90), "Virtual Mouse", (255, 255, 255), bgcolor) + win.blit(img_on_off[is_virtual_mouse], pos) + if is_virtual_mouse: + p = "%s, %s" % (virtual_x, virtual_y) + showtext(win, (pos[0] + 50, pos[1]), p, bgcolor, (255, 255, 55)) + + +def drawhistory(win, history): + img = font.render("Event History Area", 1, (155, 155, 155), (0, 0, 0)) + win.blit(img, (2, 132)) + ypos = 450 + h = list(history) + h.reverse() + for line in h: + r = win.blit(line, (10, ypos)) + win.fill(0, (r.right, r.top, 620, r.height)) + ypos -= font.get_height() + + +def draw_usage_in_history(history, text): + lines = text.split("\n") + for line in lines: + if line == "" or "===" in line: + continue + img = font.render(line, 1, (50, 200, 50), (0, 0, 0)) + history.append(img) + + +def main(): + pg.init() + print(usage) + + win = pg.display.set_mode((640, 480), pg.RESIZABLE) + pg.display.set_caption("Mouse Focus Workout. h key for help") + + global font + font = pg.font.Font(None, 26) + + global img_on_off + img_on_off.append(font.render("Off", 1, (0, 0, 0), (255, 50, 50))) + img_on_off.append(font.render("On", 1, (0, 0, 0), (50, 255, 50))) + + # stores surfaces of text representing what has gone through the event queue + history = [] + + + # let's turn on the joysticks just so we can play with em + for x in range(pg.joystick.get_count()): + j = pg.joystick.Joystick(x) + j.init() + txt = "Enabled joystick: " + j.get_name() + img = font.render(txt, 1, (50, 200, 50), (0, 0, 0)) + history.append(img) + if not pg.joystick.get_count(): + img = font.render("No Joysticks to Initialize", 1, (50, 200, 50), (0, 0, 0)) + history.append(img) + + going = True + while going: + for e in pg.event.get(): + if e.type == pg.KEYDOWN: + if e.key == pg.K_ESCAPE: + going = False + else: + global last_key + last_key = e.key + if e.key == pg.K_h: + draw_usage_in_history(history, usage) + + if e.type == pg.MOUSEBUTTONDOWN and e.button == 1: + pg.event.set_grab(not pg.event.get_grab()) + + if e.type == pg.MOUSEBUTTONDOWN and e.button == 3: + pg.mouse.set_visible(not pg.mouse.get_visible()) + + if e.type != pg.MOUSEMOTION: + txt = "%s: %s" % (pg.event.event_name(e.type), e.dict) + img = font.render(txt, 1, (50, 200, 50), (0, 0, 0)) + history.append(img) + history = history[-13:] + + if e.type == pg.VIDEORESIZE: + win = pg.display.set_mode(e.size, pg.RESIZABLE) + + if e.type == pg.QUIT: + going = False + + drawstatus(win) + drawhistory(win, history) + + pg.display.flip() + pg.time.wait(10) + + pg.quit() + raise SystemExit + + +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/fastevents.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/fastevents.py new file mode 100644 index 0000000..615e2d0 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/fastevents.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python +""" pygame.examples.fastevents + +This is a stress test for the fastevents module. + +If you are using threads, then fastevents is useful. +""" +import time as pytime +from threading import Thread + +import pygame as pg + +# the config to try different settings out with the event queues. + +# use the fastevent module or not. +event_module = pg.fastevent +# event_module = event + +# use pg.display.flip(). +# otherwise we test raw event processing throughput. +with_display = 1 + +# limit the game loop to 40 fps. +slow_tick = 0 + +NUM_EVENTS_TO_POST = 200000 + + +class PostThem(Thread): + def __init__(self): + Thread.__init__(self) + self.done = [] + self.stop = [] + + def run(self): + self.done = [] + self.stop = [] + for x in range(NUM_EVENTS_TO_POST): + ee = pg.event.Event(pg.USEREVENT) + try_post = 1 + + # the pg.event.post raises an exception if the event + # queue is full. so wait a little bit, and try again. + while try_post: + try: + event_module.post(ee) + try_post = 0 + except pg.error: + pytime.sleep(0.001) + try_post = 1 + + if self.stop: + return + self.done.append(1) + + +def main(): + pg.init() + + if hasattr(event_module, "init"): + event_module.init() + + c = pg.time.Clock() + + pg.display.set_mode((640, 480), pg.RESIZABLE) + pg.display.set_caption("fastevent Workout") + + poster = PostThem() + + t1 = pytime.time() + poster.start() + + going = True + while going: + for e in event_module.get(): + if e.type == pg.QUIT: + print(c.get_fps()) + poster.stop.append(1) + going = False + if e.type == pg.KEYDOWN: + if e.key == pg.K_ESCAPE: + print(c.get_fps()) + poster.stop.append(1) + going = False + if poster.done: + print(c.get_fps()) + print(c) + t2 = pytime.time() + print("total time:%s" % (t2 - t1)) + print("events/second:%s" % (NUM_EVENTS_TO_POST / (t2 - t1))) + going = False + if with_display: + pg.display.flip() + if slow_tick: + c.tick(40) + + pg.quit() + + +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/font_viewer.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/font_viewer.py new file mode 100644 index 0000000..9efd6fd --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/font_viewer.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python +""" pygame.examples.font_viewer +Scroll through your system fonts from a list of surfaces or one huge buffer. + +This example exhibits: +* iterate over available fonts using font.get_fonts and font.SysFont() +* scroll using virtual mouse input +* save a surface to disk +* work with a very large surface +* simple mouse and keyboard scroll speed acceleration + +By default this example uses the fonts returned by pygame.font.get_fonts() +and opens them using pygame.font.SysFont(). +Alternatively, you may pass a path to the command line. The TTF files found +in that directory will be used instead. + +Mouse Controls: +* Click in the window to toggle scrolling. The cursor will vanish and pygame + will enter virtual mouse mode until you click again. + +Keyboard Controls: +* Press up or down to scroll +* Press escape to exit +""" +import sys +import os + +import pygame as pg + +use_big_surface = False # draw into large buffer and save png file + + +class FontViewer: + """ + This example is encapsulated by the fontviewer class + It initializes the pygame window, handles input, and draws itself + to the screen. + """ + + KEY_SCROLL_SPEED = 10 + + def __init__(self, **dparams): + pg.init() + self.font_dir = dparams.get("folder", None) + + # create a window that uses 80 percent of the screen + info = pg.display.Info() + w = info.current_w + h = info.current_h + pg.display.set_mode((int(w * 0.8), int(h * 0.8))) + self.font_size = h // 20 + + self.clock = pg.time.Clock() + self.y_offset = 0 + self.grabbed = False + self.render_fonts("&N abcDEF789") + + if use_big_surface or "big" in sys.argv: + self.render_surface() + self.display_surface() + self.save_png() + else: + self.display_fonts() + + def get_font_list(self): + """ + Generate a font list using font.get_fonts() for system fonts or + from a path from the command line. + """ + path = "" + if len(sys.argv) > 1 and os.path.exists(sys.argv[1]): + path = os.path.join(sys.argv[1], "") + fonts = [] + if os.path.exists(path): + # this list comprehension could replace the following loop + # fonts = [f in os.listdir(path) if f.endswith('.ttf')] + for font in os.listdir(path): + if font.endswith(".ttf"): + fonts.append(font) + return fonts or pg.font.get_fonts(), path + + def render_fonts(self, text="A display of font &N", **dparams): + """ + Build a list that includes a surface and the running total of their + height for each font in the font list. Store the largest width and + other variables for later use. + """ + font_size = dparams.get("size", 0) or self.font_size + color = dparams.get("color", (255, 255, 255)) + self.back_color = dparams.get("back_color", (0, 0, 0)) + + fonts, path = self.get_font_list() + font_surfaces = [] + total_height = 0 + max_width = 0 + + load_font = pg.font.Font if path else pg.font.SysFont + + # display instructions at the top of the display + font = pg.font.SysFont(pg.font.get_default_font(), font_size) + lines = ( + "Click in this window to enter scroll mode", + "The mouse will be grabbed and hidden until you click again", + "Foreign fonts might render incorrectly", + "Here are your {} fonts".format(len(fonts)), + "", + ) + for line in lines: + surf = font.render(line, 1, color, self.back_color) + font_surfaces.append((surf, total_height)) + total_height += surf.get_height() + max_width = max(max_width, surf.get_width()) + + # render all the fonts and store them with the total height + for name in sorted(fonts): + try: + font = load_font(path + name, font_size) + except IOError: + continue + line = text.replace("&N", name) + print(name, line, surf.get_height()) + try: + surf = font.render(line, 1, color, self.back_color) + except pg.error as e: + print(e) + break + + max_width = max(max_width, surf.get_width()) + font_surfaces.append((surf, total_height)) + total_height += surf.get_height() + + # store variables for later usage + self.total_height = total_height + self.max_width = max_width + self.font_surfaces = font_surfaces + self.max_y = total_height - pg.display.get_surface().get_height() + + def display_fonts(self): + """ + Display the visible fonts based on the y_offset value(updated in + handle_events) and the height of the pygame window. + """ + display = pg.display.get_surface() + clock = pg.time.Clock() + center = display.get_width() // 2 + + while True: + # draw visible surfaces + display.fill(self.back_color) + for surface, top in self.font_surfaces: + if top >= self.y_offset: + x = center - surface.get_width() // 2 + display.blit(surface, (x, top - self.y_offset)) + + # get input and update the screen + if not self.handle_events(): + break + pg.display.flip() + clock.tick(30) + + def render_surface(self): + """ + Note: this method uses twice the memory and is only called if + big_surface is set to true or big is added to the command line. + + Optionally generates one large buffer to draw all the font surfaces + into. This is necessary to save the display to a png file and may + be useful for testing large surfaces. + """ + + large_surface = pg.surface.Surface( + (self.max_width, self.total_height) + ).convert() + large_surface.fill(self.back_color) + print("scrolling surface created") + + # display the surface size and memory usage + byte_size = large_surface.get_bytesize() + total_size = byte_size * (self.max_width * self.total_height) + print( + "Surface Size = {}x{} @ {}bpp: {:,.3f}mb".format( + self.max_width, self.total_height, byte_size, total_size / 1000000.0 + ) + ) + + y = 0 + center = int(self.max_width / 2) + for surface, top in self.font_surfaces: + w = surface.get_width() + x = center - int(w / 2) + large_surface.blit(surface, (x, y)) + y += surface.get_height() + self.max_y = large_surface.get_height() - pg.display.get_surface().get_height() + self.surface = large_surface + + def display_surface(self, time=10): + """ + Display the large surface created by the render_surface method. Scrolls + based on the y_offset value(set in handle_events) and the height of the + pygame window. + """ + screen = pg.display.get_surface() + + # Create a Rect equal to size of screen. Then we can just change its + # top attribute to draw the desired part of the rendered font surface + # to the display surface + rect = pg.rect.Rect( + 0, + 0, + self.surface.get_width(), + min(self.surface.get_height(), screen.get_height()), + ) + + x = int((screen.get_width() - self.surface.get_width()) / 2) + going = True + while going: + if not self.handle_events(): + going = False + screen.fill(self.back_color) + rect.top = self.y_offset + screen.blit(self.surface, (x, 0), rect) + pg.display.flip() + self.clock.tick(20) + + def save_png(self, name="font_viewer.png"): + pg.image.save(self.surface, name) + file_size = os.path.getsize(name) // 1024 + print("font surface saved to {}\nsize: {:,}Kb".format(name, file_size)) + + def handle_events(self): + """ + This method handles user input. It returns False when it receives + a pygame.QUIT event or the user presses escape. The y_offset is + changed based on mouse and keyboard input. display_fonts() and + display_surface() use the y_offset to scroll display. + """ + events = pg.event.get() + for e in events: + if e.type == pg.QUIT: + return False + elif e.type == pg.KEYDOWN: + if e.key == pg.K_ESCAPE: + return False + elif e.type == pg.MOUSEBUTTONDOWN: + # enter or exit virtual mouse mode for scrolling + self.grabbed = not self.grabbed + pg.event.set_grab(self.grabbed) + pg.mouse.set_visible(not self.grabbed) + + # allow simple accelerated scrolling with the keyboard + keys = pg.key.get_pressed() + if keys[pg.K_UP]: + self.key_held += 1 + self.y_offset -= int(self.KEY_SCROLL_SPEED * (self.key_held // 10)) + elif keys[pg.K_DOWN]: + self.key_held += 1 + self.y_offset += int(self.KEY_SCROLL_SPEED * (self.key_held // 10)) + else: + self.key_held = 20 + + # set the y_offset for scrolling and keep it between 0 and max_y + y = pg.mouse.get_rel()[1] + if y and self.grabbed: + self.y_offset += (y / 2) ** 2 * (y / abs(y)) + self.y_offset = min((max(self.y_offset, 0), self.max_y)) + return True + + +viewer = FontViewer() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/fonty.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/fonty.py new file mode 100644 index 0000000..7b5bffd --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/fonty.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +""" pygame.examples.fonty + +Here we load a .TTF True Type font file, and display it in +a basic pygame window. + +Demonstrating several Font object attributes. + +- basic window, event, and font management. +""" +import pygame as pg +from pygame.compat import unicode_ +import sys +import locale + + +if sys.version_info >= (3,): + + def print_unicode(s): + e = locale.getpreferredencoding() + print(s.encode(e, "backslashreplace").decode()) + + +else: + + def print_unicode(s): + e = locale.getpreferredencoding() + print(s.encode(e, "backslashreplace")) + + +def main(): + # initialize + pg.init() + resolution = 400, 200 + screen = pg.display.set_mode(resolution) + + ## pg.mouse.set_cursor(*pg.cursors.diamond) + + fg = 250, 240, 230 + bg = 5, 5, 5 + wincolor = 40, 40, 90 + + # fill background + screen.fill(wincolor) + + # load font, prepare values + font = pg.font.Font(None, 80) + text = "Fonty" + size = font.size(text) + + # no AA, no transparancy, normal + ren = font.render(text, 0, fg, bg) + screen.blit(ren, (10, 10)) + + # no AA, transparancy, underline + font.set_underline(1) + ren = font.render(text, 0, fg) + screen.blit(ren, (10, 40 + size[1])) + font.set_underline(0) + + a_sys_font = pg.font.SysFont("Arial", 60) + + # AA, no transparancy, bold + a_sys_font.set_bold(1) + ren = a_sys_font.render(text, 1, fg, bg) + screen.blit(ren, (30 + size[0], 10)) + a_sys_font.set_bold(0) + + # AA, transparancy, italic + a_sys_font.set_italic(1) + ren = a_sys_font.render(text, 1, fg) + screen.blit(ren, (30 + size[0], 40 + size[1])) + a_sys_font.set_italic(0) + + # Get some metrics. + print("Font metrics for 'Fonty': %s" % a_sys_font.metrics(text)) + ch = unicode_("%c") % 0x3060 + msg = unicode_("Font metrics for '%s': %s") % (ch, a_sys_font.metrics(ch)) + print_unicode(msg) + + ## #some_japanese_unicode = u"\u304b\u3070\u306b" + ##some_japanese_unicode = unicode_('%c%c%c') % (0x304b, 0x3070, 0x306b) + + # AA, transparancy, italic + ##ren = a_sys_font.render(some_japanese_unicode, 1, fg) + ##screen.blit(ren, (30 + size[0], 40 + size[1])) + + # show the surface and await user quit + pg.display.flip() + while 1: + # use event.wait to keep from polling 100% cpu + if pg.event.wait().type in (pg.QUIT, pg.KEYDOWN, pg.MOUSEBUTTONDOWN): + break + + +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/freetype_misc.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/freetype_misc.py new file mode 100644 index 0000000..39b2992 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/freetype_misc.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +""" pygame.examples.freetype_misc + + +Miscellaneous (or misc) means: + "consisting of a mixture of various things that are not + usually connected with each other" + Adjective + + +All those words you read on computers, magazines, books, and such over the years? +Probably a lot of them were constructed with... + +The FreeType Project: a free, high-quality and portable Font engine. +https://freetype.org + +Next time you're reading something. Think of them. + + +Herein lies a *BOLD* demo consisting of a mixture of various things. + + Not only is it a *BOLD* demo, it's an + italics demo, + a rotated demo, + it's a blend, + and is sized to go nicely with a cup of tea*. + + * also goes well with coffee. + +Enjoy! +""" +import os +import pygame as pg +import pygame.freetype as freetype + +colors = { + "grey_light": pg.Color(200, 200, 200), + "grey_dark": pg.Color(100, 100, 100), + "green": pg.Color(50, 255, 63), + "red": pg.Color(220, 30, 30), + "blue": pg.Color(50, 75, 245), +} + + +def run(): + pg.init() + + fontdir = os.path.dirname(os.path.abspath(__file__)) + font = freetype.Font(os.path.join(fontdir, "data", "sans.ttf")) + + screen = pg.display.set_mode((800, 600)) + screen.fill(colors["grey_light"]) + + font.underline_adjustment = 0.5 + font.pad = True + font.render_to( + screen, + (32, 32), + "Hello World", + colors["red"], + colors["grey_dark"], + size=64, + style=freetype.STYLE_UNDERLINE | freetype.STYLE_OBLIQUE, + ) + font.pad = False + + font.render_to( + screen, + (32, 128), + "abcdefghijklm", + colors["grey_dark"], + colors["green"], + size=64, + ) + + font.vertical = True + font.render_to(screen, (32, 200), "Vertical?", colors["blue"], None, size=32) + font.vertical = False + + font.render_to( + screen, (64, 190), "Let's spin!", colors["red"], None, size=48, rotation=55 + ) + + font.render_to( + screen, (160, 290), "All around!", colors["green"], None, size=48, rotation=-55 + ) + + font.render_to( + screen, (250, 220), "and BLEND", pg.Color(255, 0, 0, 128), None, size=64 + ) + + font.render_to( + screen, (265, 237), "or BLAND!", pg.Color(0, 0xCC, 28, 128), None, size=64 + ) + + # Some pinwheels + font.origin = True + for angle in range(0, 360, 45): + font.render_to( + screen, (150, 420), ")", pg.Color("black"), size=48, rotation=angle + ) + font.vertical = True + for angle in range(15, 375, 30): + font.render_to( + screen, (600, 400), "|^*", pg.Color("orange"), size=48, rotation=angle + ) + font.vertical = False + font.origin = False + + utext = pg.compat.as_unicode(r"I \u2665 Unicode") + font.render_to(screen, (298, 320), utext, pg.Color(0, 0xCC, 0xDD), None, size=64) + + utext = pg.compat.as_unicode(r"\u2665") + font.render_to( + screen, (480, 32), utext, colors["grey_light"], colors["red"], size=148 + ) + + font.render_to( + screen, + (380, 380), + "...yes, this is an SDL surface", + pg.Color(0, 0, 0), + None, + size=24, + style=freetype.STYLE_STRONG, + ) + + font.origin = True + r = font.render_to( + screen, + (100, 530), + "stretch", + pg.Color("red"), + None, + size=(24, 24), + style=freetype.STYLE_NORMAL, + ) + font.render_to( + screen, + (100 + r.width, 530), + " VERTICAL", + pg.Color("red"), + None, + size=(24, 48), + style=freetype.STYLE_NORMAL, + ) + + r = font.render_to( + screen, + (100, 580), + "stretch", + pg.Color("blue"), + None, + size=(24, 24), + style=freetype.STYLE_NORMAL, + ) + font.render_to( + screen, + (100 + r.width, 580), + " HORIZONTAL", + pg.Color("blue"), + None, + size=(48, 24), + style=freetype.STYLE_NORMAL, + ) + + pg.display.flip() + + while 1: + if pg.event.wait().type in (pg.QUIT, pg.KEYDOWN, pg.MOUSEBUTTONDOWN): + break + + pg.quit() + + +if __name__ == "__main__": + run() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/glcube.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/glcube.py new file mode 100644 index 0000000..e8a1cb2 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/glcube.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python +""" pygame.examples.glcube + +Draw a cube on the screen. + + + +Amazing. + +Every frame we orbit the camera around a small amount +creating the illusion of a spinning object. + +First we setup some points of a multicolored cube. Then we then go through +a semi-unoptimized loop to draw the cube points onto the screen. + +OpenGL does all the hard work for us. :] + + +Keyboard Controls +----------------- + +* ESCAPE key to quit +* f key to toggle fullscreen. + +""" +import pygame as pg + +try: + import OpenGL.GL as GL + import OpenGL.GLU as GLU +except ImportError: + print("The GLCUBE example requires PyOpenGL") + raise SystemExit + + +# Some simple data for a colored cube here we have the 3D point position and color +# for each corner. A list of indices describes each face, and a list of +# indicies describes each edge. + + +CUBE_POINTS = ( + (0.5, -0.5, -0.5), + (0.5, 0.5, -0.5), + (-0.5, 0.5, -0.5), + (-0.5, -0.5, -0.5), + (0.5, -0.5, 0.5), + (0.5, 0.5, 0.5), + (-0.5, -0.5, 0.5), + (-0.5, 0.5, 0.5), +) + +# colors are 0-1 floating values +CUBE_COLORS = ( + (1, 0, 0), + (1, 1, 0), + (0, 1, 0), + (0, 0, 0), + (1, 0, 1), + (1, 1, 1), + (0, 0, 1), + (0, 1, 1), +) + +CUBE_QUAD_VERTS = ( + (0, 1, 2, 3), + (3, 2, 7, 6), + (6, 7, 5, 4), + (4, 5, 1, 0), + (1, 5, 7, 2), + (4, 0, 3, 6), +) + +CUBE_EDGES = ( + (0, 1), + (0, 3), + (0, 4), + (2, 1), + (2, 3), + (2, 7), + (6, 3), + (6, 4), + (6, 7), + (5, 1), + (5, 4), + (5, 7), +) + + +def drawcube(): + "draw the cube" + allpoints = list(zip(CUBE_POINTS, CUBE_COLORS)) + + GL.glBegin(GL.GL_QUADS) + for face in CUBE_QUAD_VERTS: + for vert in face: + pos, color = allpoints[vert] + GL.glColor3fv(color) + GL.glVertex3fv(pos) + GL.glEnd() + + GL.glColor3f(1.0, 1.0, 1.0) + GL.glBegin(GL.GL_LINES) + for line in CUBE_EDGES: + for vert in line: + pos, color = allpoints[vert] + GL.glVertex3fv(pos) + + GL.glEnd() + + +def init_gl_stuff(): + + GL.glEnable(GL.GL_DEPTH_TEST) # use our zbuffer + + # setup the camera + GL.glMatrixMode(GL.GL_PROJECTION) + GL.glLoadIdentity() + GLU.gluPerspective(45.0, 640 / 480.0, 0.1, 100.0) # setup lens + GL.glTranslatef(0.0, 0.0, -3.0) # move back + GL.glRotatef(25, 1, 0, 0) # orbit higher + + +def main(): + "run the demo" + # initialize pygame and setup an opengl display + pg.init() + + fullscreen = True + pg.display.set_mode((640, 480), pg.OPENGL | pg.DOUBLEBUF | pg.FULLSCREEN) + + init_gl_stuff() + + going = True + while going: + # check for quit'n events + events = pg.event.get() + for event in events: + if event.type == pg.QUIT or ( + event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE + ): + going = False + + elif event.type == pg.KEYDOWN: + if event.key == pg.K_f: + if not fullscreen: + print("Changing to FULLSCREEN") + pg.display.set_mode( + (640, 480), pg.OPENGL | pg.DOUBLEBUF | pg.FULLSCREEN + ) + else: + print("Changing to windowed mode") + pg.display.set_mode((640, 480), pg.OPENGL | pg.DOUBLEBUF) + fullscreen = not fullscreen + init_gl_stuff() + + # clear screen and move camera + GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) + + # orbit camera around by 1 degree + GL.glRotatef(1, 0, 1, 0) + + drawcube() + pg.display.flip() + pg.time.wait(10) + + +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/headless_no_windows_needed.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/headless_no_windows_needed.py new file mode 100644 index 0000000..a74057c --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/headless_no_windows_needed.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +""" pygame.examples.headless_no_windows_needed + +How to use pygame with no windowing system, like on headless servers. + +Thumbnail generation with scaling is an example of what you can do with pygame. +NOTE: the pygame scale function uses mmx/sse if available, and can be run + in multiple threads. +""" +useage = """-scale inputimage outputimage new_width new_height +eg. -scale in.png out.png 50 50 + +""" + +import os +import sys + +# set SDL to use the dummy NULL video driver, +# so it doesn't need a windowing system. +os.environ["SDL_VIDEODRIVER"] = "dummy" + +import pygame as pg + +# Some platforms need to init the display for some parts of pg. +pg.display.init() +screen = pg.display.set_mode((1, 1)) + + +def scaleit(fin, fout, w, h): + i = pg.image.load(fin) + + if hasattr(pg.transform, "smoothscale"): + scaled_image = pg.transform.smoothscale(i, (w, h)) + else: + scaled_image = pg.transform.scale(i, (w, h)) + pg.image.save(scaled_image, fout) + + +def main(fin, fout, w, h): + """smoothscale image file named fin as fout with new size (w,h)""" + scaleit(fin, fout, w, h) + + +if __name__ == "__main__": + if "-scale" in sys.argv: + fin, fout, w, h = sys.argv[2:] + w, h = map(int, [w, h]) + main(fin, fout, w, h) + else: + print(useage) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/liquid.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/liquid.py new file mode 100644 index 0000000..ff63b92 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/liquid.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +""" pygame.examples.liquid + +This example demonstrates a simplish water effect of an +image. It attempts to create a hardware display surface that +can use pageflipping for faster updates. Note that the colormap +from the loaded GIF image is copied to the colormap for the +display surface. + +This is based on the demo named F2KWarp by Brad Graham of Freedom2000 +done in BlitzBasic. I was just translating the BlitzBasic code to +pygame to compare the results. I didn't bother porting the text and +sound stuff, that's an easy enough challenge for the reader :] +""" + +import pygame as pg +import os +from math import sin +import time + +main_dir = os.path.split(os.path.abspath(__file__))[0] + + +def main(): + # initialize and setup screen + pg.init() + screen = pg.display.set_mode((640, 480), pg.HWSURFACE | pg.DOUBLEBUF) + + # load image and quadruple + imagename = os.path.join(main_dir, "data", "liquid.bmp") + bitmap = pg.image.load(imagename) + bitmap = pg.transform.scale2x(bitmap) + bitmap = pg.transform.scale2x(bitmap) + + # get the image and screen in the same format + if screen.get_bitsize() == 8: + screen.set_palette(bitmap.get_palette()) + else: + bitmap = bitmap.convert() + + # prep some variables + anim = 0.0 + + # mainloop + xblocks = range(0, 640, 20) + yblocks = range(0, 480, 20) + stopevents = pg.QUIT, pg.KEYDOWN, pg.MOUSEBUTTONDOWN + while 1: + for e in pg.event.get(): + if e.type in stopevents: + return + + anim = anim + 0.02 + for x in xblocks: + xpos = (x + (sin(anim + x * 0.01) * 15)) + 20 + for y in yblocks: + ypos = (y + (sin(anim + y * 0.01) * 15)) + 20 + screen.blit(bitmap, (x, y), (xpos, ypos, 20, 20)) + + pg.display.flip() + time.sleep(0.01) + + +if __name__ == "__main__": + main() + + +"""BTW, here is the code from the BlitzBasic example this was derived +from. i've snipped the sound and text stuff out. +----------------------------------------------------------------- +; Brad@freedom2000.com + +; Load a bmp pic (800x600) and slice it into 1600 squares +Graphics 640,480 +SetBuffer BackBuffer() +bitmap$="f2kwarp.bmp" +pic=LoadAnimImage(bitmap$,20,15,0,1600) + +; use SIN to move all 1600 squares around to give liquid effect +Repeat +f=0:w=w+10:If w=360 Then w=0 +For y=0 To 599 Step 15 +For x = 0 To 799 Step 20 +f=f+1:If f=1600 Then f=0 +DrawBlock pic,(x+(Sin(w+x)*40))/1.7+80,(y+(Sin(w+y)*40))/1.7+60,f +Next:Next:Flip:Cls +Until KeyDown(1) +""" diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/mask.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/mask.py new file mode 100644 index 0000000..9ad01ad --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/mask.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python +""" pygame.examples.mask + +A pygame.mask collision detection production. + + + + +Brought + + to + you + by + + the + +pixels + 0000000000000 + and + 111111 + + +This is 32 bits: + 11111111111111111111111111111111 + +There are 32 or 64 bits in a computer 'word'. +Rather than using one word for a pixel, +the mask module represents 32 or 64 pixels in one word. +As you can imagine, this makes things fast, and saves memory. + +Compute intensive things like collision detection, +and computer vision benefit greatly from this. + + +This module can also be run as a stand-alone program, excepting +one or more image file names as command line arguments. +""" + +import sys +import os +import random + +import pygame as pg + + +def maskFromSurface(surface, threshold=127): + return pg.mask.from_surface(surface, threshold) + + +def vadd(x, y): + return [x[0] + y[0], x[1] + y[1]] + + +def vsub(x, y): + return [x[0] - y[0], x[1] - y[1]] + + +def vdot(x, y): + return x[0] * y[0] + x[1] * y[1] + + +class Sprite: + def __init__(self, surface, mask=None): + self.surface = surface + if mask: + self.mask = mask + else: + self.mask = maskFromSurface(self.surface) + self.setPos([0, 0]) + self.setVelocity([0, 0]) + + def setPos(self, pos): + self.pos = [pos[0], pos[1]] + + def setVelocity(self, vel): + self.vel = [vel[0], vel[1]] + + def move(self, dr): + self.pos = vadd(self.pos, dr) + + def kick(self, impulse): + self.vel[0] += impulse[0] + self.vel[1] += impulse[1] + + def collide(self, s): + """Test if the sprites are colliding and + resolve the collision in this case.""" + offset = [int(x) for x in vsub(s.pos, self.pos)] + overlap = self.mask.overlap_area(s.mask, offset) + if overlap == 0: + return + """Calculate collision normal""" + nx = self.mask.overlap_area( + s.mask, (offset[0] + 1, offset[1]) + ) - self.mask.overlap_area(s.mask, (offset[0] - 1, offset[1])) + ny = self.mask.overlap_area( + s.mask, (offset[0], offset[1] + 1) + ) - self.mask.overlap_area(s.mask, (offset[0], offset[1] - 1)) + if nx == 0 and ny == 0: + """One sprite is inside another""" + return + n = [nx, ny] + dv = vsub(s.vel, self.vel) + J = vdot(dv, n) / (2 * vdot(n, n)) + if J > 0: + """Can scale up to 2*J here to get bouncy collisions""" + J *= 1.9 + self.kick([nx * J, ny * J]) + s.kick([-J * nx, -J * ny]) + return + + # """Separate the sprites""" + # c1 = -overlap/vdot(n,n) + # c2 = -c1/2 + # self.move([c2*nx,c2*ny]) + # s.move([(c1+c2)*nx,(c1+c2)*ny]) + + def update(self, dt): + self.pos[0] += dt * self.vel[0] + self.pos[1] += dt * self.vel[1] + + +def main(*args): + """Display multiple images bounce off each other using collision detection + + Positional arguments: + one or more image file names. + + This pg.masks demo will display multiple moving sprites bouncing + off each other. More than one sprite image can be provided. + """ + + if len(args) == 0: + raise ValueError("Require at least one image file name: non given") + print("Press any key to quit") + screen = pg.display.set_mode((640, 480)) + if any("fist.bmp" in x for x in args): + pg.display.set_caption("Punch Nazis") + images = [] + masks = [] + for impath in args: + images.append(pg.image.load(impath).convert_alpha()) + masks.append(maskFromSurface(images[-1])) + + numtimes = 10 + import time + + t1 = time.time() + for x in range(numtimes): + unused_mask = maskFromSurface(images[-1]) + t2 = time.time() + + print("python maskFromSurface :%s" % (t2 - t1)) + + t1 = time.time() + for x in range(numtimes): + unused_mask = pg.mask.from_surface(images[-1]) + t2 = time.time() + + print("C pg.mask.from_surface :%s" % (t2 - t1)) + + sprites = [] + for i in range(20): + j = i % len(images) + s = Sprite(images[j], masks[j]) + s.setPos( + ( + random.uniform(0, screen.get_width()), + random.uniform(0, screen.get_height()), + ) + ) + s.setVelocity((random.uniform(-5, 5), random.uniform(-5, 5))) + sprites.append(s) + pg.time.set_timer(pg.USEREVENT, 33) + while 1: + event = pg.event.wait() + if event.type == pg.QUIT: + return + elif event.type == pg.USEREVENT: + + # Do both mechanics and screen update + screen.fill((240, 220, 100)) + for i in range(len(sprites)): + for j in range(i + 1, len(sprites)): + sprites[i].collide(sprites[j]) + for s in sprites: + s.update(1) + if s.pos[0] < -s.surface.get_width() - 3: + s.pos[0] = screen.get_width() + elif s.pos[0] > screen.get_width() + 3: + s.pos[0] = -s.surface.get_width() + if s.pos[1] < -s.surface.get_height() - 3: + s.pos[1] = screen.get_height() + elif s.pos[1] > screen.get_height() + 3: + s.pos[1] = -s.surface.get_height() + screen.blit(s.surface, s.pos) + pg.display.update() + elif event.type == pg.KEYDOWN: + return + + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: mask.py [ ...]") + print("Let many copies of IMAGE(s) bounce against each other") + print("Press any key to quit") + main_dir = os.path.split(os.path.abspath(__file__))[0] + imagename = os.path.join(main_dir, "data", "fist.bmp") + main(imagename) + else: + main(*sys.argv[1:]) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/midi.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/midi.py new file mode 100644 index 0000000..7a7be9f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/midi.py @@ -0,0 +1,878 @@ +#!/usr/bin/env python +""" pygame.examples.midi + +midi input, and a separate example of midi output. + +By default it runs the output example. + +python -m pygame.examples.midi --output +python -m pygame.examples.midi --input +python -m pygame.examples.midi --input +""" + +import sys +import os + +import pygame as pg +import pygame.midi + + +def print_device_info(): + pygame.midi.init() + _print_device_info() + pygame.midi.quit() + + +def _print_device_info(): + for i in range(pygame.midi.get_count()): + r = pygame.midi.get_device_info(i) + (interf, name, input, output, opened) = r + + in_out = "" + if input: + in_out = "(input)" + if output: + in_out = "(output)" + + print( + "%2i: interface :%s:, name :%s:, opened :%s: %s" + % (i, interf, name, opened, in_out) + ) + + +def input_main(device_id=None): + pg.init() + pg.fastevent.init() + event_get = pg.fastevent.get + event_post = pg.fastevent.post + + pygame.midi.init() + + _print_device_info() + + if device_id is None: + input_id = pygame.midi.get_default_input_id() + else: + input_id = device_id + + print("using input_id :%s:" % input_id) + i = pygame.midi.Input(input_id) + + pg.display.set_mode((1, 1)) + + going = True + while going: + events = event_get() + for e in events: + if e.type in [pg.QUIT]: + going = False + if e.type in [pg.KEYDOWN]: + going = False + if e.type in [pygame.midi.MIDIIN]: + print(e) + + if i.poll(): + midi_events = i.read(10) + # convert them into pygame events. + midi_evs = pygame.midi.midis2events(midi_events, i.device_id) + + for m_e in midi_evs: + event_post(m_e) + + del i + pygame.midi.quit() + + +def output_main(device_id=None): + """Execute a musical keyboard example for the Church Organ instrument + + This is a piano keyboard example, with a two octave keyboard, starting at + note F3. Left mouse down over a key starts a note, left up stops it. The + notes are also mapped to the computer keyboard keys, assuming an American + English PC keyboard (sorry everyone else, but I don't know if I can map to + absolute key position instead of value.) The white keys are on the second + row, TAB to BACKSLASH, starting with note F3. The black keys map to the top + row, '1' to BACKSPACE, starting with F#3. 'r' is middle C. Close the + window or press ESCAPE to quit the program. Key velocity (note + amplitude) varies vertically on the keyboard image, with minimum velocity + at the top of a key and maximum velocity at bottom. + + Default Midi output, no device_id given, is to the default output device + for the computer. + + """ + + # A note to new pygamers: + # + # All the midi module stuff is in this function. It is unnecessary to + # understand how the keyboard display works to appreciate how midi + # messages are sent. + + # The keyboard is drawn by a Keyboard instance. This instance maps Midi + # notes to musical keyboard keys. A regions surface maps window position + # to (Midi note, velocity) pairs. A key_mapping dictionary does the same + # for computer keyboard keys. Midi sound is controlled with direct method + # calls to a pygame.midi.Output instance. + # + # Things to consider when using pygame.midi: + # + # 1) Initialize the midi module with a to pygame.midi.init(). + # 2) Create a midi.Output instance for the desired output device port. + # 3) Select instruments with set_instrument() method calls. + # 4) Play notes with note_on() and note_off() method calls. + # 5) Call pygame.midi.Quit() when finished. Though the midi module tries + # to ensure that midi is properly shut down, it is best to do it + # explicitly. A try/finally statement is the safest way to do this. + # + + # GRAND_PIANO = 0 + CHURCH_ORGAN = 19 + + instrument = CHURCH_ORGAN + # instrument = GRAND_PIANO + start_note = 53 # F3 (white key note), start_note != 0 + n_notes = 24 # Two octaves (14 white keys) + + bg_color = Color("slategray") + + key_mapping = make_key_mapping( + [ + pg.K_TAB, + pg.K_1, + pg.K_q, + pg.K_2, + pg.K_w, + pg.K_3, + pg.K_e, + pg.K_r, + pg.K_5, + pg.K_t, + pg.K_6, + pg.K_y, + pg.K_u, + pg.K_8, + pg.K_i, + pg.K_9, + pg.K_o, + pg.K_0, + pg.K_p, + pg.K_LEFTBRACKET, + pg.K_EQUALS, + pg.K_RIGHTBRACKET, + pg.K_BACKSPACE, + pg.K_BACKSLASH, + ], + start_note, + ) + + pg.init() + pygame.midi.init() + + _print_device_info() + + if device_id is None: + port = pygame.midi.get_default_output_id() + else: + port = device_id + + print("using output_id :%s:" % port) + + midi_out = pygame.midi.Output(port, 0) + try: + midi_out.set_instrument(instrument) + keyboard = Keyboard(start_note, n_notes) + + screen = pg.display.set_mode(keyboard.rect.size) + screen.fill(bg_color) + pg.display.flip() + + background = pg.Surface(screen.get_size()) + background.fill(bg_color) + dirty_rects = [] + keyboard.draw(screen, background, dirty_rects) + pg.display.update(dirty_rects) + + regions = pg.Surface(screen.get_size()) # initial color (0,0,0) + keyboard.map_regions(regions) + + pg.event.set_blocked(pg.MOUSEMOTION) + mouse_note = 0 + on_notes = set() + while 1: + e = pg.event.wait() + if e.type == pg.MOUSEBUTTONDOWN: + mouse_note, velocity, __, __ = regions.get_at(e.pos) + if mouse_note and mouse_note not in on_notes: + keyboard.key_down(mouse_note) + midi_out.note_on(mouse_note, velocity) + on_notes.add(mouse_note) + else: + mouse_note = 0 + elif e.type == pg.MOUSEBUTTONUP: + if mouse_note: + midi_out.note_off(mouse_note) + keyboard.key_up(mouse_note) + on_notes.remove(mouse_note) + mouse_note = 0 + elif e.type == pg.QUIT: + break + elif e.type == pg.KEYDOWN: + if e.key == pg.K_ESCAPE: + break + try: + note, velocity = key_mapping[e.key] + except KeyError: + pass + else: + if note not in on_notes: + keyboard.key_down(note) + midi_out.note_on(note, velocity) + on_notes.add(note) + elif e.type == pg.KEYUP: + try: + note, __ = key_mapping[e.key] + except KeyError: + pass + else: + if note in on_notes and note != mouse_note: + keyboard.key_up(note) + midi_out.note_off(note, 0) + on_notes.remove(note) + + dirty_rects = [] + keyboard.draw(screen, background, dirty_rects) + pg.display.update(dirty_rects) + finally: + del midi_out + pygame.midi.quit() + + +def make_key_mapping(key_list, start_note): + """Return a dictionary of (note, velocity) by computer keyboard key code""" + + mapping = {} + for i in range(len(key_list)): + mapping[key_list[i]] = (start_note + i, 127) + return mapping + + +class NullKey(object): + """A dummy key that ignores events passed to it by other keys + + A NullKey instance is the left key instance used by default + for the left most keyboard key. + + """ + + def _right_white_down(self): + pass + + def _right_white_up(self): + pass + + def _right_black_down(self): + pass + + def _right_black_up(self): + pass + + +null_key = NullKey() + + +def key_class(updates, image_strip, image_rects, is_white_key=True): + """Return a keyboard key widget class + + Arguments: + updates - a set into which a key instance adds itself if it needs + redrawing. + image_strip - The surface containing the images of all key states. + image_rects - A list of Rects giving the regions within image_strip that + are relevant to this key class. + is_white_key (default True) - Set false if this is a black key. + + This function automates the creation of a key widget class for the + three basic key types. A key has two basic states, up or down ( + depressed). Corresponding up and down images are drawn for each + of these two states. But to give the illusion of depth, a key + may have shadows cast upon it by the adjacent keys to its right. + These shadows change depending on the up/down state of the key and + its neighbors. So a key may support multiple images and states + depending on the shadows. A key type is determined by the length + of image_rects and the value of is_white. + + """ + + # Naming convention: Variables used by the Key class as part of a + # closure start with 'c_'. + + # State logic and shadows: + # + # A key may cast a shadow upon the key to its left. A black key casts a + # shadow on an adjacent white key. The shadow changes depending of whether + # the black or white key is depressed. A white key casts a shadow on the + # white key to its left if it is up and the left key is down. Therefore + # a keys state, and image it will draw, is determined entirely by its + # itself and the key immediately adjacent to it on the right. A white key + # is always assumed to have an adjacent white key. + # + # There can be up to eight key states, representing all permutations + # of the three fundamental states of self up/down, adjacent white + # right up/down, adjacent black up/down. + # + down_state_none = 0 + down_state_self = 1 + down_state_white = down_state_self << 1 + down_state_self_white = down_state_self | down_state_white + down_state_black = down_state_white << 1 + down_state_self_black = down_state_self | down_state_black + down_state_white_black = down_state_white | down_state_black + down_state_all = down_state_self | down_state_white_black + + # Some values used in the class. + # + c_down_state_initial = down_state_none + c_down_state_rect_initial = image_rects[0] + c_updates = updates + c_image_strip = image_strip + c_width, c_height = image_rects[0].size + + # A key propagates its up/down state change to the adjacent white key on + # the left by calling the adjacent key's _right_black_down or + # _right_white_down method. + # + if is_white_key: + key_color = "white" + else: + key_color = "black" + c_notify_down_method = "_right_%s_down" % key_color + c_notify_up_method = "_right_%s_up" % key_color + + # Images: + # + # A black key only needs two images, for the up and down states. Its + # appearance is unaffected by the adjacent keys to its right, which cast no + # shadows upon it. + # + # A white key with a no adjacent black to its right only needs three + # images, for self up, self down, and both self and adjacent white down. + # + # A white key with both a black and white key to its right needs six + # images: self up, self up and adjacent black down, self down, self and + # adjacent white down, self and adjacent black down, and all three down. + # + # Each 'c_event' dictionary maps the current key state to a new key state, + # along with corresponding image, for the related event. If no redrawing + # is required for the state change then the image rect is simply None. + # + c_event_down = {down_state_none: (down_state_self, image_rects[1])} + c_event_up = {down_state_self: (down_state_none, image_rects[0])} + c_event_right_white_down = { + down_state_none: (down_state_none, None), + down_state_self: (down_state_self, None), + } + c_event_right_white_up = c_event_right_white_down.copy() + c_event_right_black_down = c_event_right_white_down.copy() + c_event_right_black_up = c_event_right_white_down.copy() + if len(image_rects) > 2: + c_event_down[down_state_white] = (down_state_self_white, image_rects[2]) + c_event_up[down_state_self_white] = (down_state_white, image_rects[0]) + c_event_right_white_down[down_state_none] = (down_state_white, None) + c_event_right_white_down[down_state_self] = ( + down_state_self_white, + image_rects[2], + ) + c_event_right_white_up[down_state_white] = (down_state_none, None) + c_event_right_white_up[down_state_self_white] = ( + down_state_self, + image_rects[1], + ) + c_event_right_black_down[down_state_white] = (down_state_white, None) + c_event_right_black_down[down_state_self_white] = (down_state_self_white, None) + c_event_right_black_up[down_state_white] = (down_state_white, None) + c_event_right_black_up[down_state_self_white] = (down_state_self_white, None) + if len(image_rects) > 3: + c_event_down[down_state_black] = (down_state_self_black, image_rects[4]) + c_event_down[down_state_white_black] = (down_state_all, image_rects[5]) + c_event_up[down_state_self_black] = (down_state_black, image_rects[3]) + c_event_up[down_state_all] = (down_state_white_black, image_rects[3]) + c_event_right_white_down[down_state_black] = (down_state_white_black, None) + c_event_right_white_down[down_state_self_black] = ( + down_state_all, + image_rects[5], + ) + c_event_right_white_up[down_state_white_black] = (down_state_black, None) + c_event_right_white_up[down_state_all] = (down_state_self_black, image_rects[4]) + c_event_right_black_down[down_state_none] = (down_state_black, image_rects[3]) + c_event_right_black_down[down_state_self] = ( + down_state_self_black, + image_rects[4], + ) + c_event_right_black_down[down_state_white] = ( + down_state_white_black, + image_rects[3], + ) + c_event_right_black_down[down_state_self_white] = ( + down_state_all, + image_rects[5], + ) + c_event_right_black_up[down_state_black] = (down_state_none, image_rects[0]) + c_event_right_black_up[down_state_self_black] = ( + down_state_self, + image_rects[1], + ) + c_event_right_black_up[down_state_white_black] = ( + down_state_white, + image_rects[0], + ) + c_event_right_black_up[down_state_all] = (down_state_self_white, image_rects[2]) + + class Key(object): + """A key widget, maintains key state and draws the key's image + + Constructor arguments: + ident - A unique key identifier. Any immutable type suitable as a key. + posn - The location of the key on the display surface. + key_left - Optional, the adjacent white key to the left. Changes in + up and down state are propagated to that key. + + A key has an associated position and state. Related to state is the + image drawn. State changes are managed with method calls, one method + per event type. The up and down event methods are public. Other + internal methods are for passing on state changes to the key_left + key instance. + + """ + + is_white = is_white_key + + def __init__(self, ident, posn, key_left=None): + """Return a new Key instance + + The initial state is up, with all adjacent keys to the right also + up. + + """ + if key_left is None: + key_left = null_key + rect = pg.Rect(posn[0], posn[1], c_width, c_height) + self.rect = rect + self._state = c_down_state_initial + self._source_rect = c_down_state_rect_initial + self._ident = ident + self._hash = hash(ident) + self._notify_down = getattr(key_left, c_notify_down_method) + self._notify_up = getattr(key_left, c_notify_up_method) + self._key_left = key_left + self._background_rect = pg.Rect(rect.left, rect.bottom - 10, c_width, 10) + c_updates.add(self) + + def down(self): + """Signal that this key has been depressed (is down)""" + + self._state, source_rect = c_event_down[self._state] + if source_rect is not None: + self._source_rect = source_rect + c_updates.add(self) + self._notify_down() + + def up(self): + """Signal that this key has been released (is up)""" + + self._state, source_rect = c_event_up[self._state] + if source_rect is not None: + self._source_rect = source_rect + c_updates.add(self) + self._notify_up() + + def _right_white_down(self): + """Signal that the adjacent white key has been depressed + + This method is for internal propagation of events between + key instances. + + """ + + self._state, source_rect = c_event_right_white_down[self._state] + if source_rect is not None: + self._source_rect = source_rect + c_updates.add(self) + + def _right_white_up(self): + """Signal that the adjacent white key has been released + + This method is for internal propagation of events between + key instances. + + """ + + self._state, source_rect = c_event_right_white_up[self._state] + if source_rect is not None: + self._source_rect = source_rect + c_updates.add(self) + + def _right_black_down(self): + """Signal that the adjacent black key has been depressed + + This method is for internal propagation of events between + key instances. + + """ + + self._state, source_rect = c_event_right_black_down[self._state] + if source_rect is not None: + self._source_rect = source_rect + c_updates.add(self) + + def _right_black_up(self): + """Signal that the adjacent black key has been released + + This method is for internal propagation of events between + key instances. + + """ + + self._state, source_rect = c_event_right_black_up[self._state] + if source_rect is not None: + self._source_rect = source_rect + c_updates.add(self) + + def __eq__(self, other): + """True if same identifiers""" + + return self._ident == other._ident + + def __hash__(self): + """Return the immutable hash value""" + + return self._hash + + def __str__(self): + """Return the key's identifier and position as a string""" + + return "" % (self._ident, self.rect.top, self.rect.left) + + def draw(self, surf, background, dirty_rects): + """Redraw the key on the surface surf + + The background is redrawn. The altered region is added to the + dirty_rects list. + + """ + + surf.blit(background, self._background_rect, self._background_rect) + surf.blit(c_image_strip, self.rect, self._source_rect) + dirty_rects.append(self.rect) + + return Key + + +def key_images(): + """Return a keyboard keys image strip and a mapping of image locations + + The return tuple is a surface and a dictionary of rects mapped to key + type. + + This function encapsulates the constants relevant to the keyboard image + file. There are five key types. One is the black key. The other four + white keys are determined by the proximity of the black keys. The plain + white key has no black key adjacent to it. A white-left and white-right + key has a black key to the left or right of it respectively. A white-center + key has a black key on both sides. A key may have up to six related + images depending on the state of adjacent keys to its right. + + """ + + my_dir = os.path.split(os.path.abspath(__file__))[0] + strip_file = os.path.join(my_dir, "data", "midikeys.png") + white_key_width = 42 + white_key_height = 160 + black_key_width = 22 + black_key_height = 94 + strip = pg.image.load(strip_file) + names = [ + "black none", + "black self", + "white none", + "white self", + "white self-white", + "white-left none", + "white-left self", + "white-left black", + "white-left self-black", + "white-left self-white", + "white-left all", + "white-center none", + "white-center self", + "white-center black", + "white-center self-black", + "white-center self-white", + "white-center all", + "white-right none", + "white-right self", + "white-right self-white", + ] + rects = {} + for i in range(2): + rects[names[i]] = pg.Rect( + i * white_key_width, 0, black_key_width, black_key_height + ) + for i in range(2, len(names)): + rects[names[i]] = pg.Rect( + i * white_key_width, 0, white_key_width, white_key_height + ) + return strip, rects + + +class Keyboard(object): + """Musical keyboard widget + + Constructor arguments: + start_note: midi note value of the starting note on the keyboard. + n_notes: number of notes (keys) on the keyboard. + + A Keyboard instance draws the musical keyboard and maintains the state of + all the keyboard keys. Individual keys can be in a down (depressed) or + up (released) state. + + """ + + _image_strip, _rects = key_images() + + white_key_width, white_key_height = _rects["white none"].size + black_key_width, black_key_height = _rects["black none"].size + + _updates = set() + + # There are five key classes, representing key shape: + # black key (BlackKey), plain white key (WhiteKey), white key to the left + # of a black key (WhiteKeyLeft), white key between two black keys + # (WhiteKeyCenter), and white key to the right of a black key + # (WhiteKeyRight). + BlackKey = key_class( + _updates, _image_strip, [_rects["black none"], _rects["black self"]], False + ) + WhiteKey = key_class( + _updates, + _image_strip, + [_rects["white none"], _rects["white self"], _rects["white self-white"]], + ) + WhiteKeyLeft = key_class( + _updates, + _image_strip, + [ + _rects["white-left none"], + _rects["white-left self"], + _rects["white-left self-white"], + _rects["white-left black"], + _rects["white-left self-black"], + _rects["white-left all"], + ], + ) + WhiteKeyCenter = key_class( + _updates, + _image_strip, + [ + _rects["white-center none"], + _rects["white-center self"], + _rects["white-center self-white"], + _rects["white-center black"], + _rects["white-center self-black"], + _rects["white-center all"], + ], + ) + WhiteKeyRight = key_class( + _updates, + _image_strip, + [ + _rects["white-right none"], + _rects["white-right self"], + _rects["white-right self-white"], + ], + ) + + def __init__(self, start_note, n_notes): + """Return a new Keyboard instance with n_note keys""" + + self._start_note = start_note + self._end_note = start_note + n_notes - 1 + self._add_keys() + + def _add_keys(self): + """Populate the keyboard with key instances + + Set the _keys and rect attributes. + + """ + + # Keys are entered in a list, where index is Midi note. Since there are + # only 128 possible Midi notes the list length is managable. Unassigned + # note positions should never be accessed, so are set None to ensure + # the bug is quickly detected. + # + key_map = [None] * 128 + + start_note = self._start_note + end_note = self._end_note + black_offset = self.black_key_width // 2 + prev_white_key = None + x = y = 0 + if is_white_key(start_note): + is_prev_white = True + else: + x += black_offset + is_prev_white = False + for note in range(start_note, end_note + 1): + ident = note # For now notes uniquely identify keyboard keys. + if is_white_key(note): + if is_prev_white: + if note == end_note or is_white_key(note + 1): + key = self.WhiteKey(ident, (x, y), prev_white_key) + else: + key = self.WhiteKeyLeft(ident, (x, y), prev_white_key) + else: + if note == end_note or is_white_key(note + 1): + key = self.WhiteKeyRight(ident, (x, y), prev_white_key) + else: + key = self.WhiteKeyCenter(ident, (x, y), prev_white_key) + is_prev_white = True + x += self.white_key_width + prev_white_key = key + else: + key = self.BlackKey(ident, (x - black_offset, y), prev_white_key) + is_prev_white = False + key_map[note] = key + self._keys = key_map + + kb_width = key_map[self._end_note].rect.right + kb_height = self.white_key_height + self.rect = pg.Rect(0, 0, kb_width, kb_height) + + def map_regions(self, regions): + """Draw the key regions onto surface regions. + + Regions must have at least 3 byte pixels. Each pixel of the keyboard + rectangle is set to the color (note, velocity, 0). The regions surface + must be at least as large as (0, 0, self.rect.left, self.rect.bottom) + + """ + + # First draw the white key regions. Then add the overlapping + # black key regions. + # + cutoff = self.black_key_height + black_keys = [] + for note in range(self._start_note, self._end_note + 1): + key = self._keys[note] + if key.is_white: + fill_region(regions, note, key.rect, cutoff) + else: + black_keys.append((note, key)) + for note, key in black_keys: + fill_region(regions, note, key.rect, cutoff) + + def draw(self, surf, background, dirty_rects): + """Redraw all altered keyboard keys""" + + changed_keys = self._updates + while changed_keys: + changed_keys.pop().draw(surf, background, dirty_rects) + + def key_down(self, note): + """Signal a key down event for note""" + + self._keys[note].down() + + def key_up(self, note): + """Signal a key up event for note""" + + self._keys[note].up() + + +def fill_region(regions, note, rect, cutoff): + """Fill the region defined by rect with a (note, velocity, 0) color + + The velocity varies from a small value at the top of the region to + 127 at the bottom. The vertical region 0 to cutoff is split into + three parts, with velocities 42, 84 and 127. Everything below cutoff + has velocity 127. + + """ + + x, y, width, height = rect + if cutoff is None: + cutoff = height + delta_height = cutoff // 3 + regions.fill((note, 42, 0), (x, y, width, delta_height)) + regions.fill((note, 84, 0), (x, y + delta_height, width, delta_height)) + regions.fill( + (note, 127, 0), (x, y + 2 * delta_height, width, height - 2 * delta_height) + ) + + +def is_white_key(note): + """True if note is represented by a white key""" + + key_pattern = [ + True, + False, + True, + True, + False, + True, + False, + True, + True, + False, + True, + False, + ] + return key_pattern[(note - 21) % len(key_pattern)] + + +def usage(): + print("--input [device_id] : Midi message logger") + print("--output [device_id] : Midi piano keyboard") + print("--list : list available midi devices") + + +def main(mode="output", device_id=None): + """Run a Midi example + + Arguments: + mode - if 'output' run a midi keyboard output example + 'input' run a midi event logger input example + 'list' list available midi devices + (default 'output') + device_id - midi device number; if None then use the default midi input or + output device for the system + + """ + + if mode == "input": + input_main(device_id) + elif mode == "output": + output_main(device_id) + elif mode == "list": + print_device_info() + else: + raise ValueError("Unknown mode option '%s'" % mode) + + +if __name__ == "__main__": + + try: + device_id = int(sys.argv[-1]) + except ValueError: + device_id = None + + if "--input" in sys.argv or "-i" in sys.argv: + + input_main(device_id) + + elif "--output" in sys.argv or "-o" in sys.argv: + output_main(device_id) + elif "--list" in sys.argv or "-l" in sys.argv: + print_device_info() + else: + usage() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/moveit.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/moveit.py new file mode 100644 index 0000000..1d30260 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/moveit.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +""" pygame.examples.moveit + +This is the full and final example from the Pygame Tutorial, +"How Do I Make It Move". It creates 10 objects and animates +them on the screen. + +Note it's a bit scant on error checking, but it's easy to read. :] +Fortunately, this is python, and we needn't wrestle with a pile of +error codes. +""" +import os +import pygame as pg + +main_dir = os.path.split(os.path.abspath(__file__))[0] + +# our game object class +class GameObject: + def __init__(self, image, height, speed): + self.speed = speed + self.image = image + self.pos = image.get_rect().move(0, height) + + def move(self): + self.pos = self.pos.move(self.speed, 0) + if self.pos.right > 600: + self.pos.left = 0 + + +# quick function to load an image +def load_image(name): + path = os.path.join(main_dir, "data", name) + return pg.image.load(path).convert() + + +# here's the full code +def main(): + pg.init() + screen = pg.display.set_mode((640, 480)) + + player = load_image("player1.gif") + background = load_image("liquid.bmp") + + # scale the background image so that it fills the window and + # successfully overwrites the old sprite position. + background = pg.transform.scale2x(background) + background = pg.transform.scale2x(background) + + screen.blit(background, (0, 0)) + + objects = [] + for x in range(10): + o = GameObject(player, x * 40, x) + objects.append(o) + + while 1: + for event in pg.event.get(): + if event.type in (pg.QUIT, pg.KEYDOWN): + return + + for o in objects: + screen.blit(background, o.pos, o.pos) + for o in objects: + o.move() + screen.blit(o.image, o.pos) + + pg.display.update() + + +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/music_drop_fade.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/music_drop_fade.py new file mode 100644 index 0000000..c9a9309 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/music_drop_fade.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python +""" pygame.examples.music_drop_fade +Fade in and play music from a list while observing several events + +Adds music files to a playlist whenever played by one of the following methods +Music files passed from the commandline are played +Music files and filenames are played when drag and dropped onto the pygame window +Polls the clipboard and plays music files if it finds one there + +Keyboard Controls: +* Press space or enter to pause music playback +* Press up or down to change the music volume +* Press left or right to seek 5 seconds into the track +* Press escape to quit +* Press any other button to skip to the next music file in the list +""" + +import pygame as pg +import os, sys + +VOLUME_CHANGE_AMOUNT = 0.02 # how fast should up and down arrows change the volume? + + +def add_file(filename): + """ + This function will check if filename exists and is a music file + If it is the file will be added to a list of music files(even if already there) + Type checking is by the extension of the file, not by its contents + We can only discover if the file is valid when we mixer.music.load() it later + + It looks in the file directory and its data subdirectory + """ + if filename.rpartition(".")[2].lower() not in music_file_types: + print("{} not added to file list".format(filename)) + print("only these files types are allowed: ", music_file_types) + return False + elif os.path.exists(filename): + music_file_list.append(filename) + elif os.path.exists(os.path.join(main_dir, filename)): + music_file_list.append(os.path.join(main_dir, filename)) + elif os.path.exists(os.path.join(data_dir, filename)): + music_file_list.append(os.path.join(data_dir, filename)) + else: + print("file not found") + return False + print("{} added to file list".format(filename)) + return True + + +def play_file(filename): + """ + This function will call add_file and play it if successful + The music will fade in during the first 4 seconds + set_endevent is used to post a MUSIC_DONE event when the song finishes + The main loop will call play_next() when the MUSIC_DONE event is received + """ + global starting_pos + + if add_file(filename): + try: # we must do this in case the file is not a valid audio file + pg.mixer.music.load(music_file_list[-1]) + except pg.error as e: + print(e) # print description such as 'Not an Ogg Vorbis audio stream' + if filename in music_file_list: + music_file_list.remove(filename) + print("{} removed from file list".format(filename)) + return + pg.mixer.music.play(fade_ms=4000) + pg.mixer.music.set_volume(volume) + + if filename.rpartition(".")[2].lower() in music_can_seek: + print("file supports seeking") + starting_pos = 0 + else: + print("file does not support seeking") + starting_pos = -1 + pg.mixer.music.set_endevent(MUSIC_DONE) + + +def play_next(): + """ + This function will play the next song in music_file_list + It uses pop(0) to get the next song and then appends it to the end of the list + The song will fade in during the first 4 seconds + """ + + global starting_pos + if len(music_file_list) > 1: + nxt = music_file_list.pop(0) + + try: + pg.mixer.music.load(nxt) + except pg.error as e: + print(e) + print("{} removed from file list".format(nxt)) + + music_file_list.append(nxt) + print("starting next song: ", nxt) + else: + nxt = music_file_list[0] + pg.mixer.music.play(fade_ms=4000) + pg.mixer.music.set_volume(volume) + pg.mixer.music.set_endevent(MUSIC_DONE) + + if nxt.rpartition(".")[2].lower() in music_can_seek: + starting_pos = 0 + else: + starting_pos = -1 + + +def draw_text_line(text, y=0): + """ + Draws a line of text onto the display surface + The text will be centered horizontally at the given y postition + The text's height is added to y and returned to the caller + """ + screen = pg.display.get_surface() + surf = font.render(text, 1, (255, 255, 255)) + y += surf.get_height() + x = (screen.get_width() - surf.get_width()) / 2 + screen.blit(surf, (x, y)) + return y + + +def change_music_postion(amount): + """ + Changes current playback postition by amount seconds. + This only works with OGG and MP3 files. + music.get_pos() returns how many milliseconds the song has played, not + the current postion in the file. We must track the starting postion + ourselves. music.set_pos() will set the position in seconds. + """ + global starting_pos + + if starting_pos >= 0: # will be -1 unless play_file() was OGG or MP3 + played_for = pg.mixer.music.get_pos() / 1000.0 + old_pos = starting_pos + played_for + starting_pos = old_pos + amount + pg.mixer.music.play(start=starting_pos) + print("jumped from {} to {}".format(old_pos, starting_pos)) + + +MUSIC_DONE = pg.event.custom_type() # event to be set as mixer.music.set_endevent() +main_dir = os.path.split(os.path.abspath(__file__))[0] +data_dir = os.path.join(main_dir, "data") + +starting_pos = 0 # needed to fast forward and rewind +volume = 0.75 +music_file_list = [] +music_file_types = ("mp3", "ogg", "mid", "mod", "it", "xm", "wav") +music_can_seek = ("mp3", "ogg", "mod", "it", "xm") + + +def main(): + global font # this will be used by the draw_text_line function + global volume, starting_pos + running = True + paused = False + + # we will be polling for key up and key down events + # users should be able to change the volume by holding the up and down arrows + # the change_volume variable will be set by key down events and cleared by key up events + change_volume = 0 + + pg.init() + pg.display.set_mode((640, 480)) + font = pg.font.SysFont("Arial", 24) + clock = pg.time.Clock() + + pg.scrap.init() + pg.SCRAP_TEXT = pg.scrap.get_types()[0] # TODO remove when scrap module is fixed + clipped = pg.scrap.get(pg.SCRAP_TEXT).decode( + "UTF-8") # store the current text from the clipboard TODO remove decode + + # add the command line arguments to the music_file_list + for arg in sys.argv[1:]: + add_file(arg) + play_file("house_lo.ogg") # play default music included with pygame + + # draw instructions on screen + y = draw_text_line("Drop music files or path names onto this window", 20) + y = draw_text_line("Copy file names into the clipboard", y) + y = draw_text_line("Or feed them from the command line", y) + y = draw_text_line("If it's music it will play!", y) + y = draw_text_line("SPACE to pause or UP/DOWN to change volume", y) + y = draw_text_line("LEFT and RIGHT will skip around the track", y) + draw_text_line("Other keys will start the next track", y) + + """ + This is the main loop + It will respond to drag and drop, clipboard changes, and key presses + """ + while running: + for ev in pg.event.get(): + if ev.type == pg.QUIT: + running = False + elif ev.type == pg.DROPTEXT: + play_file(ev.text) + elif ev.type == pg.DROPFILE: + play_file(ev.file) + elif ev.type == MUSIC_DONE: + play_next() + elif ev.type == pg.KEYDOWN: + if ev.key == pg.K_ESCAPE: + running = False # exit loop + elif ev.key in (pg.K_SPACE, pg.K_RETURN): + if paused: + pg.mixer.music.unpause() + paused = False + else: + pg.mixer.music.pause() + paused = True + elif ev.key == pg.K_UP: + change_volume = VOLUME_CHANGE_AMOUNT + elif ev.key == pg.K_DOWN: + change_volume = -VOLUME_CHANGE_AMOUNT + elif ev.key == pg.K_RIGHT: + change_music_postion(+5) + elif ev.key == pg.K_LEFT: + change_music_postion(-5) + + else: + play_next() + + elif ev.type == pg.KEYUP: + if ev.key in (pg.K_UP, pg.K_DOWN): + change_volume = 0 + + # is the user holding up or down? + if change_volume: + volume += change_volume + volume = min(max(0, volume), 1) # volume should be between 0 and 1 + pg.mixer.music.set_volume(volume) + print("volume:", volume) + + # TODO remove decode when SDL2 scrap is fixed + new_text = pg.scrap.get(pg.SCRAP_TEXT).decode("UTF-8") + if new_text != clipped: # has the clipboard changed? + clipped = new_text + play_file(clipped) # try to play the file if it has + + pg.display.flip() + clock.tick(9) # keep CPU use down by updating screen less often + + pg.quit() + + +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/overlay.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/overlay.py new file mode 100644 index 0000000..32b187b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/overlay.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +""" pygame.examples.overlay + +The overlay module is deprecated now. +It is an olden days way to draw video quickly. +""" +import sys +import pygame as pg +from pygame.compat import xrange_ + +SR = (800, 600) +ovl = None + +######################################################################## +# Simple video player +def vPlayer(fName): + global ovl + f = open(fName, "rb") + fmt = f.readline().strip() + res = f.readline().strip() + unused_col = f.readline().strip() + if fmt != "P5": + print("Unknown format( len %d ). Exiting..." % len(fmt)) + return + + w, h = [int(x) for x in res.split(" ")] + h = (h * 2) / 3 + # Read into strings + y = f.read(w * h) + u = [] + v = [] + for _ in xrange_(0, h / 2): + u.append(f.read(w / 2)) + v.append(f.read(w / 2)) + + u = "".join(u) + v = "".join(v) + + # Open overlay with the resolution specified + ovl = pg.Overlay(pg.YV12_OVERLAY, (w, h)) + ovl.set_location(0, 0, w, h) + + ovl.display((y, u, v)) + while 1: + pg.time.wait(10) + for ev in pg.event.get(): + if ev.type in (pg.KEYDOWN, pg.QUIT): + return + + +def main(fname): + """play video file fname""" + pg.init() + try: + pg.display.set_mode(SR) + vPlayer(fname) + finally: + pg.quit() + + +# Test all modules +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Usage: play_file ") + else: + main(sys.argv[1]) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/pixelarray.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/pixelarray.py new file mode 100644 index 0000000..1230f89 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/pixelarray.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python +""" pygame.examples.pixelarray + +PixelArray does array processing of pixels. +Sort of like another array processor called 'numpy' - But for pixels. + + Flip it, + stripe it, + rotate it. + +Controls +-------- + +To see different effects - press a key or click a mouse. +""" +import os +import pygame as pg +from pygame.compat import xrange_ + +main_dir = os.path.split(os.path.abspath(__file__))[0] +data_dir = os.path.join(main_dir, "data") + + +def show(image): + screen = pg.display.get_surface() + screen.fill((255, 255, 255)) + screen.blit(image, (0, 0)) + pg.display.flip() + while 1: + event = pg.event.wait() + if event.type == pg.QUIT: + raise SystemExit + if event.type in [pg.MOUSEBUTTONDOWN, pg.KEYDOWN]: + break + + +def main(): + pg.init() + + pg.display.set_mode((255, 255)) + surface = pg.Surface((255, 255)) + + pg.display.flip() + + # Create the PixelArray. + ar = pg.PixelArray(surface) + + # Do some easy gradient effect. + for y in xrange_(255): + r, g, b = y, y, y + ar[:, y] = (r, g, b) + del ar + show(surface) + + # We have made some gradient effect, now flip it. + ar = pg.PixelArray(surface) + ar[:] = ar[:, ::-1] + del ar + show(surface) + + # Every second column will be made blue + ar = pg.PixelArray(surface) + ar[::2] = (0, 0, 255) + del ar + show(surface) + + # Every second row will be made green + ar = pg.PixelArray(surface) + ar[:, ::2] = (0, 255, 0) + del ar + show(surface) + + # Manipulate the image. Flip it around the y axis. + surface = pg.image.load(os.path.join(data_dir, "arraydemo.bmp")) + ar = pg.PixelArray(surface) + ar[:] = ar[:, ::-1] + del ar + show(surface) + + # Flip the image around the x axis. + ar = pg.PixelArray(surface) + ar[:] = ar[::-1, :] + del ar + show(surface) + + # Every second column will be made white. + ar = pg.PixelArray(surface) + ar[::2] = (255, 255, 255) + del ar + show(surface) + + # Flip the image around both axes, restoring it's original layout. + ar = pg.PixelArray(surface) + ar[:] = ar[::-1, ::-1] + del ar + show(surface) + + # Rotate 90 degrees clockwise. + w, h = surface.get_size() + surface2 = pg.Surface((h, w), surface.get_flags(), surface) + ar = pg.PixelArray(surface) + ar2 = pg.PixelArray(surface2) + ar2[...] = ar.transpose()[::-1, :] + del ar, ar2 + show(surface2) + + # Scale it by throwing each second pixel away. + surface = pg.image.load(os.path.join(data_dir, "arraydemo.bmp")) + ar = pg.PixelArray(surface) + sf2 = ar[::2, ::2].make_surface() + del ar + show(sf2) + + # Replace anything looking like the blue color from the text. + ar = pg.PixelArray(surface) + ar.replace((60, 60, 255), (0, 255, 0), 0.06) + del ar + show(surface) + + # Extract anything which might be somewhat black. + surface = pg.image.load(os.path.join(data_dir, "arraydemo.bmp")) + ar = pg.PixelArray(surface) + ar2 = ar.extract((0, 0, 0), 0.07) + sf2 = ar2.surface + del ar, ar2 + show(sf2) + + # Compare two images. + surface = pg.image.load(os.path.join(data_dir, "alien1.gif")) + surface2 = pg.image.load(os.path.join(data_dir, "alien2.gif")) + ar1 = pg.PixelArray(surface) + ar2 = pg.PixelArray(surface2) + ar3 = ar1.compare(ar2, 0.07) + sf3 = ar3.surface + del ar1, ar2, ar3 + show(sf3) + + +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/playmus.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/playmus.py new file mode 100644 index 0000000..ee5212f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/playmus.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python +""" pygame.examples.playmus + +A simple music player. + + Use pygame.mixer.music to play an audio file. + +A window is created to handle keyboard events for playback commands. + + +Keyboard Controls +----------------- + +space - play/pause toggle +r - rewind +f - fade out +q - stop + +""" +import sys + +import pygame as pg +import pygame.freetype + + +class Window(object): + """The application's Pygame window + + A Window instance manages the creation of and drawing to a + window. It is a singleton class. Only one instance can exist. + + """ + + instance = None + + def __new__(cls, *args, **kwds): + """Return an open Pygame window""" + + if Window.instance is not None: + return Window.instance + self = object.__new__(cls) + pg.display.init() + self.screen = pg.display.set_mode((600, 400)) + Window.instance = self + return self + + def __init__(self, title): + pg.display.set_caption(title) + self.screen.fill(pg.Color("white")) + pg.display.flip() + + pygame.freetype.init() + self.font = pygame.freetype.Font(None, 20) + self.font.origin = True + self.ascender = int(self.font.get_sized_ascender() * 1.5) + self.descender = int(self.font.get_sized_descender() * 1.5) + self.line_height = self.ascender - self.descender + + self.write_lines( + "'q', ESCAPE or close this window to quit\n" + "SPACE to play/pause\n" + "'r' to rewind\n" + "'f' to faid out over 5 seconds\n", + 0, + ) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + return False + + def close(self): + pg.display.quit() + Window.instance = None + + def write_lines(self, text, line=0): + w, h = self.screen.get_size() + line_height = self.line_height + nlines = h // line_height + if line < 0: + line = nlines + line + for i, text_line in enumerate(text.split("\n"), line): + y = i * line_height + self.ascender + # Clear the line first. + self.screen.fill(pg.Color("white"), (0, i * line_height, w, line_height)) + + # Write new text. + self.font.render_to(self.screen, (15, y), text_line, pg.Color("blue")) + pg.display.flip() + + +def show_usage_message(): + print("Usage: python playmus.py ") + print(" python -m pygame.examples.playmus ") + + +def main(file_path): + """Play an audio file with pg.mixer.music""" + + with Window(file_path) as win: + win.write_lines("Loading ...", -1) + pg.mixer.init(frequency=44100) + try: + paused = False + pg.mixer.music.load(file_path) + + # Make sure the event loop ticks over at least every 0.5 seconds. + pg.time.set_timer(pg.USEREVENT, 500) + + pg.mixer.music.play() + win.write_lines("Playing ...\n", -1) + + while pg.mixer.music.get_busy(): + e = pg.event.wait() + if e.type == pg.KEYDOWN: + key = e.key + if key == pg.K_SPACE: + if paused: + pg.mixer.music.unpause() + paused = False + win.write_lines("Playing ...\n", -1) + else: + pg.mixer.music.pause() + paused = True + win.write_lines("Paused ...\n", -1) + elif key == pg.K_r: + pg.mixer.music.rewind() + if paused: + win.write_lines("Rewound.", -1) + elif key == pg.K_f: + win.write_lines("Fading out ...\n", -1) + pg.mixer.music.fadeout(5000) + # when finished get_busy() will return 0. + elif key in [pg.K_q, pg.K_ESCAPE]: + pg.mixer.music.stop() + # get_busy() will now return 0. + elif e.type == pg.QUIT: + pg.mixer.music.stop() + # get_busy() will now return 0. + pg.time.set_timer(pg.USEREVENT, 0) + finally: + pg.mixer.quit() + + +if __name__ == "__main__": + # Check the only command line argument, a file path + if len(sys.argv) != 2: + show_usage_message() + else: + main(sys.argv[1]) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/prevent_display_stretching.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/prevent_display_stretching.py new file mode 100644 index 0000000..efcacac --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/prevent_display_stretching.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +""" pygame.examples.prevent_display_stretching + +Prevent display stretching on Windows. + +On some computers, the display environment can be configured to stretch +all windows so that they will not appear too small on the screen for +the user. This configuration is especially common on high-DPI displays. +pygame graphics appear distorted when automatically stretched by the +display environment. This script demonstrates a technique for preventing +this stretching and distortion. + +Limitations: +This script makes an API call that is only available on Windows (versions +Vista and newer). + +""" + +# Ensure that the computer is running Windows Vista or newer +import os +import sys + +if os.name != "nt" or sys.getwindowsversion()[0] < 6: + raise NotImplementedError("this script requires Windows Vista or newer") + +import pygame as pg + +import ctypes + +# Determine whether or not the user would like to prevent stretching +if os.path.basename(sys.executable) == "pythonw.exe": + selection = "y" +else: + from pygame.compat import raw_input_ + + selection = None + while selection not in ("y", "n"): + selection = raw_input_("Prevent stretching? (y/n): ").strip().lower() + +if selection == "y": + msg = "Stretching is prevented." +else: + msg = "Stretching is not prevented." + +# Prevent stretching +if selection == "y": + user32 = ctypes.windll.user32 + user32.SetProcessDPIAware() + +# Show screen +pg.display.init() +RESOLUTION = (350, 350) +screen = pg.display.set_mode(RESOLUTION) + +# Render message onto a surface +pg.font.init() +font = pg.font.Font(None, 36) +msg_surf = font.render(msg, 1, pg.Color("green")) +res_surf = font.render("Intended resolution: %ix%i" % RESOLUTION, 1, pg.Color("green")) + +# Control loop +running = True +clock = pg.time.Clock() +counter = 0 +while running: + + for event in pg.event.get(): + if event.type == pg.QUIT: + running = False + + screen.fill(pg.Color("black")) + + # Draw lines which will be blurry if the window is stretched + # or clear if the window is not stretched. + pg.draw.line(screen, pg.Color("white"), (0, counter), (RESOLUTION[0] - 1, counter)) + pg.draw.line(screen, pg.Color("white"), (counter, 0), (counter, RESOLUTION[1] - 1)) + + # Blit message onto screen surface + msg_blit_rect = screen.blit(msg_surf, (0, 0)) + screen.blit(res_surf, (0, msg_blit_rect.bottom)) + + clock.tick(10) + + pg.display.flip() + + counter += 1 + if counter == RESOLUTION[0]: + counter = 0 diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/scaletest.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/scaletest.py new file mode 100644 index 0000000..dc402ec --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/scaletest.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python +""" pygame.examples.scaletest + +Shows an interactive image scaler. + +""" +import sys +import time +import pygame as pg + + +def main(imagefile, convert_alpha=False, run_speed_test=False): + """show an interactive image scaler + + Args: + imagefile - name of source image (required) + convert_alpha - use convert_alpha() on the surf (default False) + run_speed_test - (default False) + """ + + # initialize display + pg.display.init() + # load background image + background = pg.image.load(imagefile) + + if run_speed_test: + if convert_alpha: + # convert_alpha() requires the display mode to be set + pg.display.set_mode((1, 1)) + background = background.convert_alpha() + + SpeedTest(background) + return + + # start fullscreen mode + screen = pg.display.set_mode((1024, 768), pg.FULLSCREEN) + if convert_alpha: + background = background.convert_alpha() + + # turn off the mouse pointer + pg.mouse.set_visible(0) + # main loop + bRunning = True + bUp = False + bDown = False + bLeft = False + bRight = False + cursize = [background.get_width(), background.get_height()] + while bRunning: + image = pg.transform.smoothscale(background, cursize) + imgpos = image.get_rect(centerx=512, centery=384) + screen.fill((255, 255, 255)) + screen.blit(image, imgpos) + pg.display.flip() + for event in pg.event.get(): + if event.type == pg.QUIT or ( + event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE + ): + bRunning = False + if event.type == pg.KEYDOWN: + if event.key == pg.K_UP: + bUp = True + if event.key == pg.K_DOWN: + bDown = True + if event.key == pg.K_LEFT: + bLeft = True + if event.key == pg.K_RIGHT: + bRight = True + if event.type == pg.KEYUP: + if event.key == pg.K_UP: + bUp = False + if event.key == pg.K_DOWN: + bDown = False + if event.key == pg.K_LEFT: + bLeft = False + if event.key == pg.K_RIGHT: + bRight = False + if bUp: + cursize[1] -= 2 + if cursize[1] < 1: + cursize[1] = 1 + if bDown: + cursize[1] += 2 + if bLeft: + cursize[0] -= 2 + if cursize[0] < 1: + cursize[0] = 1 + if bRight: + cursize[0] += 2 + + +def SpeedTest(image): + print("\nImage Scaling Speed Test - Image Size %s\n" % str(image.get_size())) + + imgsize = [image.get_width(), image.get_height()] + duration = 0.0 + for i in range(128): + shrinkx = (imgsize[0] * i) // 128 + shrinky = (imgsize[1] * i) // 128 + start = time.time() + tempimg = pg.transform.smoothscale(image, (shrinkx, shrinky)) + duration += time.time() - start + del tempimg + + print( + "Average transform.smoothscale shrink time: %.4f ms." % (duration / 128 * 1000) + ) + + duration = 0.0 + for i in range(128): + expandx = (imgsize[0] * (i + 129)) // 128 + expandy = (imgsize[1] * (i + 129)) // 128 + start = time.time() + tempimg = pg.transform.smoothscale(image, (expandx, expandy)) + duration += time.time() - start + del tempimg + + print( + "Average transform.smoothscale expand time: %.4f ms." % (duration / 128 * 1000) + ) + + duration = 0.0 + for i in range(128): + shrinkx = (imgsize[0] * i) // 128 + shrinky = (imgsize[1] * i) // 128 + start = time.time() + tempimg = pg.transform.scale(image, (shrinkx, shrinky)) + duration += time.time() - start + del tempimg + + print("Average transform.scale shrink time: %.4f ms." % (duration / 128 * 1000)) + + duration = 0.0 + for i in range(128): + expandx = (imgsize[0] * (i + 129)) // 128 + expandy = (imgsize[1] * (i + 129)) // 128 + start = time.time() + tempimg = pg.transform.scale(image, (expandx, expandy)) + duration += time.time() - start + del tempimg + + print("Average transform.scale expand time: %.4f ms." % (duration / 128 * 1000)) + + +if __name__ == "__main__": + # check input parameters + if len(sys.argv) < 2: + print("\nUsage: %s imagefile [-t] [-convert_alpha]" % sys.argv[0]) + print(" imagefile image filename (required)") + print(" -t run speed test") + print(" -convert_alpha use convert_alpha() on the image's " "surface\n") + else: + main( + sys.argv[1], + convert_alpha="-convert_alpha" in sys.argv, + run_speed_test="-t" in sys.argv, + ) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/scrap_clipboard.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/scrap_clipboard.py new file mode 100644 index 0000000..2a1ce28 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/scrap_clipboard.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +""" pygame.examples.scrap_clipboard + +Demonstrates the clipboard capabilities of pygame. + +Copy/paste! + + +Keyboard Controls +----------------- + +g - get and print types in clipboard. If, image blit to screen. +p - place some text into clipboard +a - print types available in the clipboard +i - put image into the clipboard +""" +import os + +import pygame as pg +import pygame.scrap as scrap +from pygame.compat import as_bytes + +BytesIO = pygame.compat.get_BytesIO() + + +def usage(): + print("Press the 'g' key to get all of the current clipboard data") + print("Press the 'p' key to put a string into the clipboard") + print("Press the 'a' key to get a list of the currently available types") + print("Press the 'i' key to put an image into the clipboard") + + +main_dir = os.path.split(os.path.abspath(__file__))[0] + +pg.init() +screen = pg.display.set_mode((200, 200)) +c = pg.time.Clock() +going = True + +# Initialize the scrap module and use the clipboard mode. +scrap.init() +scrap.set_mode(SCRAP_CLIPBOARD) + +usage() + +while going: + for e in pg.event.get(): + if e.type == QUIT or (e.type == pg.KEYDOWN and e.key == pg.K_ESCAPE): + going = False + + elif e.type == KEYDOWN and e.key == pg.K_g: + # This means to look for data. + print("Getting the different clipboard data..") + for t in scrap.get_types(): + r = scrap.get(t) + if r and len(r) > 500: + print("Type %s : (large %i byte buffer)" % (t, len(r))) + elif r is None: + print("Type %s : None" % (t,)) + else: + print("Type %s : '%s'" % (t, r.decode("ascii", "ignore"))) + if "image" in t: + namehint = t.split("/")[1] + if namehint in ["bmp", "png", "jpg"]: + f = BytesIO(r) + loaded_surf = pg.image.load(f, "." + namehint) + screen.blit(loaded_surf, (0, 0)) + + elif e.type == KEYDOWN and e.key == pg.K_p: + # Place some text into the selection. + print("Placing clipboard text.") + scrap.put(SCRAP_TEXT, as_bytes("Hello. This is a message from scrap.")) + + elif e.type == KEYDOWN and e.key == pg.K_a: + # Get all available types. + print("Getting the available types from the clipboard.") + types = scrap.get_types() + print(types) + if len(types) > 0: + print("Contains %s: %s" % (types[0], scrap.contains(types[0]))) + print("Contains _INVALID_: ", scrap.contains("_INVALID_")) + + elif e.type == KEYDOWN and e.key == pg.K_i: + print("Putting image into the clipboard.") + scrap.set_mode(SCRAP_CLIPBOARD) + fp = open(os.path.join(main_dir, "data", "liquid.bmp"), "rb") + buf = fp.read() + scrap.put("image/bmp", buf) + fp.close() + + elif e.type in (pg.KEYDOWN, pg.MOUSEBUTTONDOWN): + usage() + pg.display.flip() + c.tick(40) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/scroll.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/scroll.py new file mode 100644 index 0000000..649c2fb --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/scroll.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python +""" pygame.examples.scroll + +An zoomed image viewer that demonstrates Surface.scroll + +This example shows a scrollable image that has a zoom factor of eight. +It uses the Surface.scroll function to shift the image on the display +surface. A clip rectangle protects a margin area. If called as a function, +the example accepts an optional image file path. If run as a program +it takes an optional file path command line argument. If no file +is provided a default image file is used. + +When running click on a black triangle to move one pixel in the direction +the triangle points. Or use the arrow keys. Close the window or press ESC +to quit. +""" +import sys +import os + +import pygame as pg +from pygame.transform import scale + +main_dir = os.path.dirname(os.path.abspath(__file__)) + +DIR_UP = 1 +DIR_DOWN = 2 +DIR_LEFT = 3 +DIR_RIGHT = 4 + +zoom_factor = 8 + + +def draw_arrow(surf, color, posn, direction): + x, y = posn + if direction == DIR_UP: + pointlist = ((x - 29, y + 30), (x + 30, y + 30), (x + 1, y - 29), (x, y - 29)) + elif direction == DIR_DOWN: + pointlist = ((x - 29, y - 29), (x + 30, y - 29), (x + 1, y + 30), (x, y + 30)) + elif direction == DIR_LEFT: + pointlist = ((x + 30, y - 29), (x + 30, y + 30), (x - 29, y + 1), (x - 29, y)) + else: + pointlist = ((x - 29, y - 29), (x - 29, y + 30), (x + 30, y + 1), (x + 30, y)) + pg.draw.polygon(surf, color, pointlist) + + +def add_arrow_button(screen, regions, posn, direction): + draw_arrow(screen, pg.Color("black"), posn, direction) + draw_arrow(regions, (direction, 0, 0), posn, direction) + + +def scroll_view(screen, image, direction, view_rect): + src_rect = None + zoom_view_rect = screen.get_clip() + image_w, image_h = image.get_size() + if direction == DIR_UP: + if view_rect.top > 0: + screen.scroll(dy=zoom_factor) + view_rect.move_ip(0, -1) + src_rect = view_rect.copy() + src_rect.h = 1 + dst_rect = zoom_view_rect.copy() + dst_rect.h = zoom_factor + elif direction == DIR_DOWN: + if view_rect.bottom < image_h: + screen.scroll(dy=-zoom_factor) + view_rect.move_ip(0, 1) + src_rect = view_rect.copy() + src_rect.h = 1 + src_rect.bottom = view_rect.bottom + dst_rect = zoom_view_rect.copy() + dst_rect.h = zoom_factor + dst_rect.bottom = zoom_view_rect.bottom + elif direction == DIR_LEFT: + if view_rect.left > 0: + screen.scroll(dx=zoom_factor) + view_rect.move_ip(-1, 0) + src_rect = view_rect.copy() + src_rect.w = 1 + dst_rect = zoom_view_rect.copy() + dst_rect.w = zoom_factor + elif direction == DIR_RIGHT: + if view_rect.right < image_w: + screen.scroll(dx=-zoom_factor) + view_rect.move_ip(1, 0) + src_rect = view_rect.copy() + src_rect.w = 1 + src_rect.right = view_rect.right + dst_rect = zoom_view_rect.copy() + dst_rect.w = zoom_factor + dst_rect.right = zoom_view_rect.right + if src_rect is not None: + scale(image.subsurface(src_rect), dst_rect.size, screen.subsurface(dst_rect)) + pg.display.update(zoom_view_rect) + + +def main(image_file=None): + if image_file is None: + image_file = os.path.join(main_dir, "data", "arraydemo.bmp") + margin = 80 + view_size = (30, 20) + zoom_view_size = (view_size[0] * zoom_factor, view_size[1] * zoom_factor) + win_size = (zoom_view_size[0] + 2 * margin, zoom_view_size[1] + 2 * margin) + background_color = pg.Color("beige") + + pg.init() + + # set up key repeating so we can hold down the key to scroll. + old_k_delay, old_k_interval = pg.key.get_repeat() + pg.key.set_repeat(500, 30) + + try: + screen = pg.display.set_mode(win_size) + screen.fill(background_color) + pg.display.flip() + + image = pg.image.load(image_file).convert() + image_w, image_h = image.get_size() + + if image_w < view_size[0] or image_h < view_size[1]: + print("The source image is too small for this example.") + print("A %i by %i or larger image is required." % zoom_view_size) + return + + regions = pg.Surface(win_size, 0, 24) + add_arrow_button(screen, regions, (40, win_size[1] // 2), DIR_LEFT) + add_arrow_button( + screen, regions, (win_size[0] - 40, win_size[1] // 2), DIR_RIGHT + ) + add_arrow_button(screen, regions, (win_size[0] // 2, 40), DIR_UP) + add_arrow_button( + screen, regions, (win_size[0] // 2, win_size[1] - 40), DIR_DOWN + ) + pg.display.flip() + + screen.set_clip((margin, margin, zoom_view_size[0], zoom_view_size[1])) + + view_rect = pg.Rect(0, 0, view_size[0], view_size[1]) + + scale( + image.subsurface(view_rect), + zoom_view_size, + screen.subsurface(screen.get_clip()), + ) + pg.display.flip() + + # the direction we will scroll in. + direction = None + + clock = pg.time.Clock() + clock.tick() + + going = True + while going: + # wait for events before doing anything. + # events = [pg.event.wait()] + pg.event.get() + events = pg.event.get() + + for e in events: + if e.type == pg.KEYDOWN: + if e.key == pg.K_ESCAPE: + going = False + elif e.key == pg.K_DOWN: + scroll_view(screen, image, DIR_DOWN, view_rect) + elif e.key == pg.K_UP: + scroll_view(screen, image, DIR_UP, view_rect) + elif e.key == pg.K_LEFT: + scroll_view(screen, image, DIR_LEFT, view_rect) + elif e.key == pg.K_RIGHT: + scroll_view(screen, image, DIR_RIGHT, view_rect) + elif e.type == pg.QUIT: + going = False + elif e.type == pg.MOUSEBUTTONDOWN: + direction = regions.get_at(e.pos)[0] + elif e.type == pg.MOUSEBUTTONUP: + direction = None + + if direction: + scroll_view(screen, image, direction, view_rect) + clock.tick(30) + + finally: + pg.key.set_repeat(old_k_delay, old_k_interval) + pg.quit() + + +if __name__ == "__main__": + if len(sys.argv) > 1: + image_file = sys.argv[1] + else: + image_file = None + main(image_file) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/setmodescale.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/setmodescale.py new file mode 100644 index 0000000..8599410 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/setmodescale.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +""" pygame.examples.setmodescale + +On high resolution displays(4k, 1080p) and tiny graphics games (640x480) +show up very small so that they are unplayable. SCALED scales up the window +for you. The game thinks it's a 640x480 window, but really it can be bigger. +Mouse events are scaled for you, so your game doesn't need to do it. + +Passing SCALED to pygame.display.set_mode means the resolution depends +on desktop size and the graphics are scaled. +""" +import pygame as pg + +pg.init() + +RES = (160, 120) +FPS = 30 +clock = pg.time.Clock() + +print ("desktops", pg.display.get_desktop_sizes()) +screen = pg.display.set_mode(RES, pg.SCALED | pg.RESIZABLE) + +# MAIN LOOP + +done = False + +i = 0 +j = 0 + +r_name, r_flags = pg.display.get_renderer_info() +print("renderer:", r_name, "flags:", bin(r_flags)) +for flag, name in [(1, "software"), (2, "accelerated"), (4, "VSync"), (8, "render to texture")]: + if (flag & r_flags): + print(name) + +while not done: + for event in pg.event.get(): + if event.type == pg.KEYDOWN and event.key == pg.K_q: + done = True + if event.type == pg.QUIT: + done = True + if event.type == pg.KEYDOWN and event.key == pg.K_f: + pg.display.toggle_fullscreen() + if event.type == pg.VIDEORESIZE: + pg.display.resize_event(event) + + i += 1 + i = i % screen.get_width() + j += i % 2 + j = j % screen.get_height() + + screen.fill((255, 0, 255)) + pg.draw.circle(screen, (0, 0, 0), (100, 100), 20) + pg.draw.circle(screen, (0, 0, 200), (0, 0), 10) + pg.draw.circle(screen, (200, 0, 0), (160, 120), 30) + pg.draw.line(screen, (250, 250, 0), (0, 120), (160, 0)) + pg.draw.circle(screen, (255, 255, 255), (i, j), 5) + + pg.display.flip() + clock.tick(FPS) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/sound.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/sound.py new file mode 100644 index 0000000..01beabb --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/sound.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +""" pygame.examples.sound + +Playing a soundfile and waiting for it to finish. You'll need the +pygame.mixer module for this to work. Note how in this simple example +we don't even bother loading all of the pygame package. +Just pick the mixer for sound and time for the delay function. + +Optional command line argument: audio file name +""" +import os +import sys +import pygame as pg + +main_dir = os.path.split(os.path.abspath(__file__))[0] + + +def main(file_path=None): + """Play an audio file as a buffered sound sample + + :param str file_path: audio file (default data/secosmic_low.wav) + """ + # choose a desired audio format + pg.mixer.init(11025) # raises exception on fail + + # load the sound + sound = pg.mixer.Sound(file_path) + + # start playing + print("Playing Sound...") + channel = sound.play() + + # poll until finished + while channel.get_busy(): # still playing + print(" ...still going...") + pg.time.wait(1000) + print("...Finished") + + +if __name__ == "__main__": + if len(sys.argv) > 1: + main(sys.argv[1]) + else: + main(os.path.join(main_dir, "data", "secosmic_lo.wav")) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/sound_array_demos.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/sound_array_demos.py new file mode 100644 index 0000000..9b4ef06 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/sound_array_demos.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python +""" pygame.examples.sound_array_demos + +Creates an echo effect on any Sound object. + +Uses sndarray and numpy to create offset faded copies of the +original sound. Currently it just uses hardcoded values for the +number of echos and the delay. Easy for you to recreate as +needed. + +version 2. changes: +- Should work with different sample rates now. +- put into a function. +- Uses numpy by default, but falls back on Numeric. +""" +import os +import pygame as pg +from numpy import zeros, int32, int16 +import time + + +# pg.mixer.init(44100, -16, 0) +pg.mixer.init() +# pg.mixer.init(11025, -16, 0) +# pg.mixer.init(11025) + + +def make_echo(sound, samples_per_second, mydebug=True): + """ returns a sound which is echoed of the last one. + """ + + echo_length = 3.5 + + a1 = pg.sndarray.array(sound) + if mydebug: + print("SHAPE1: %s" % (a1.shape,)) + + length = a1.shape[0] + + # myarr = zeros(length+12000) + myarr = zeros(a1.shape, int32) + + if len(a1.shape) > 1: + # mult = a1.shape[1] + size = (a1.shape[0] + int(echo_length * a1.shape[0]), a1.shape[1]) + # size = (a1.shape[0] + int(a1.shape[0] + (echo_length * 3000)), a1.shape[1]) + else: + # mult = 1 + size = (a1.shape[0] + int(echo_length * a1.shape[0]),) + # size = (a1.shape[0] + int(a1.shape[0] + (echo_length * 3000)),) + + if mydebug: + print(int(echo_length * a1.shape[0])) + myarr = zeros(size, int32) + + if mydebug: + print("size %s" % (size,)) + print(myarr.shape) + myarr[:length] = a1 + # print (myarr[3000:length+3000]) + # print (a1 >> 1) + # print ("a1.shape %s" % (a1.shape,)) + # c = myarr[3000:length+(3000*mult)] + # print ("c.shape %s" % (c.shape,)) + + incr = int(samples_per_second / echo_length) + gap = length + + myarr[incr : gap + incr] += a1 >> 1 + myarr[incr * 2 : gap + (incr * 2)] += a1 >> 2 + myarr[incr * 3 : gap + (incr * 3)] += a1 >> 3 + myarr[incr * 4 : gap + (incr * 4)] += a1 >> 4 + + if mydebug: + print("SHAPE2: %s" % (myarr.shape,)) + + sound2 = pg.sndarray.make_sound(myarr.astype(int16)) + + return sound2 + + +def slow_down_sound(sound, rate): + """ returns a sound which is a slowed down version of the original. + rate - at which the sound should be slowed down. eg. 0.5 would be half speed. + """ + + raise NotImplementedError() + # grow_rate = 1 / rate + # make it 1/rate times longer. + # a1 = pg.sndarray.array(sound) + # surf = pg.surfarray.make_surface(a1) + # print (a1.shape[0] * grow_rate) + # scaled_surf = pg.transform.scale(surf, (int(a1.shape[0] * grow_rate), a1.shape[1])) + # print (scaled_surf) + # print (surf) + + # a2 = a1 * rate + # print (a1.shape) + # print (a2.shape) + # print (a2) + # sound2 = pg.sndarray.make_sound(a2.astype(int16)) + # return sound2 + + +def sound_from_pos(sound, start_pos, samples_per_second=None, inplace=1): + """ returns a sound which begins at the start_pos. + start_pos - in seconds from the begining. + samples_per_second - + """ + + # see if we want to reuse the sound data or not. + if inplace: + a1 = pg.sndarray.samples(sound) + else: + a1 = pg.sndarray.array(sound) + + # see if samples per second has been given. If not, query the pg.mixer. + # eg. it might be set to 22050 + if samples_per_second is None: + samples_per_second = pg.mixer.get_init()[0] + + # figure out the start position in terms of samples. + start_pos_in_samples = int(start_pos * samples_per_second) + + # cut the beginning off the sound at the start position. + a2 = a1[start_pos_in_samples:] + + # make the Sound instance from the array. + sound2 = pg.sndarray.make_sound(a2) + + return sound2 + + +def main(): + """play various sndarray effects + """ + + main_dir = os.path.split(os.path.abspath(__file__))[0] + print("mixer.get_init %s" % (pg.mixer.get_init(),)) + + samples_per_second = pg.mixer.get_init()[0] + + print(("-" * 30) + "\n") + print("loading sound") + sound = pg.mixer.Sound(os.path.join(main_dir, "data", "car_door.wav")) + + print("-" * 30) + print("start positions") + print("-" * 30) + + start_pos = 0.1 + sound2 = sound_from_pos(sound, start_pos, samples_per_second) + + print("sound.get_length %s" % (sound.get_length(),)) + print("sound2.get_length %s" % (sound2.get_length(),)) + sound2.play() + while pg.mixer.get_busy(): + pg.time.wait(200) + + print("waiting 2 seconds") + pg.time.wait(2000) + print("playing original sound") + + sound.play() + while pg.mixer.get_busy(): + pg.time.wait(200) + + print("waiting 2 seconds") + pg.time.wait(2000) + + # if 0: + # #TODO: this is broken. + # print (("-" * 30) + "\n") + # print ("Slow down the original sound.") + # rate = 0.2 + # slowed_sound = slow_down_sound(sound, rate) + # slowed_sound.play() + # while pg.mixer.get_busy(): + # pg.time.wait(200) + + print("-" * 30) + print("echoing") + print("-" * 30) + + t1 = time.time() + sound2 = make_echo(sound, samples_per_second) + print("time to make echo %i" % (time.time() - t1,)) + + print("original sound") + sound.play() + while pg.mixer.get_busy(): + pg.time.wait(200) + + print("echoed sound") + sound2.play() + while pg.mixer.get_busy(): + pg.time.wait(200) + + sound = pg.mixer.Sound(os.path.join(main_dir, "data", "secosmic_lo.wav")) + + t1 = time.time() + sound3 = make_echo(sound, samples_per_second) + print("time to make echo %i" % (time.time() - t1,)) + + print("original sound") + sound.play() + while pg.mixer.get_busy(): + pg.time.wait(200) + + print("echoed sound") + sound3.play() + while pg.mixer.get_busy(): + pg.time.wait(200) + + +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/sprite_texture.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/sprite_texture.py new file mode 100644 index 0000000..e8cfdfe --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/sprite_texture.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +""" pygame.examples.sprite_texture + +Experimental! Uses APIs which may disapear in the next release (_sdl2 is private). + + +Hardware accelerated Image objects with pygame.sprite. + +_sdl2.video.Image is a backwards compatible way with to use Texture with +pygame.sprite groups. +""" +import os +import pygame as pg + +if pg.get_sdl_version()[0] < 2: + raise SystemExit("This example requires pygame 2 and SDL2.") +from pygame._sdl2 import Window, Texture, Image, Renderer + + +data_dir = os.path.join(os.path.split(os.path.abspath(__file__))[0], "data") + + +def load_img(file): + return pg.image.load(os.path.join(data_dir, file)) + + +pg.display.init() +pg.key.set_repeat(10, 10) + +win = Window("asdf", resizable=True) +renderer = Renderer(win) +tex = Texture.from_surface(renderer, load_img("alien1.gif")) + + +class Something(pg.sprite.Sprite): + def __init__(self, img): + pg.sprite.Sprite.__init__(self) + + self.rect = img.get_rect() + self.image = img + + self.rect.w *= 5 + self.rect.h *= 5 + + img.origin = self.rect.w / 2, self.rect.h / 2 + + +sprite = Something(Image(tex, (0, 0, tex.width / 2, tex.height / 2))) +sprite.rect.x = 250 +sprite.rect.y = 50 + +# sprite2 = Something(Image(sprite.image)) +sprite2 = Something(Image(tex)) +sprite2.rect.x = 250 +sprite2.rect.y = 250 +sprite2.rect.w /= 2 +sprite2.rect.h /= 2 + +group = pg.sprite.Group() +group.add(sprite2) +group.add(sprite) + +import math + +t = 0 +running = True +clock = pg.time.Clock() +renderer.draw_color = (255, 0, 0, 255) + +while running: + for event in pg.event.get(): + if event.type == pg.QUIT: + running = False + elif event.type == pg.KEYDOWN: + if event.key == pg.K_ESCAPE: + running = False + elif event.key == pg.K_LEFT: + sprite.rect.x -= 5 + elif event.key == pg.K_RIGHT: + sprite.rect.x += 5 + elif event.key == pg.K_DOWN: + sprite.rect.y += 5 + elif event.key == pg.K_UP: + sprite.rect.y -= 5 + + renderer.clear() + t += 1 + + img = sprite.image + img.angle += 1 + img.flipX = t % 50 < 25 + img.flipY = t % 100 < 50 + img.color[0] = int(255.0 * (0.5 + math.sin(0.5 * t + 10.0) / 2.0)) + img.alpha = int(255.0 * (0.5 + math.sin(0.1 * t) / 2.0)) + # img.draw(dstrect=(x, y, 5 * img.srcrect['w'], 5 * img.srcrect['h'])) + + group.draw(renderer) + + renderer.present() + + clock.tick(60) + win.title = str("FPS: {}".format(clock.get_fps())) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/stars.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/stars.py new file mode 100644 index 0000000..390b85f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/stars.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +""" pg.examples.stars + + We are all in the gutter, + but some of us are looking at the stars. + -- Oscar Wilde + +A simple starfield example. Note you can move the 'center' of +the starfield by leftclicking in the window. This example show +the basics of creating a window, simple pixel plotting, and input +event management. +""" +import random +import math +import pygame as pg + +# constants +WINSIZE = [640, 480] +WINCENTER = [320, 240] +NUMSTARS = 150 + + +def init_star(): + "creates new star values" + dir = random.randrange(100000) + velmult = random.random() * 0.6 + 0.4 + vel = [math.sin(dir) * velmult, math.cos(dir) * velmult] + return vel, WINCENTER[:] + + +def initialize_stars(): + "creates a new starfield" + stars = [] + for x in range(NUMSTARS): + star = init_star() + vel, pos = star + steps = random.randint(0, WINCENTER[0]) + pos[0] = pos[0] + (vel[0] * steps) + pos[1] = pos[1] + (vel[1] * steps) + vel[0] = vel[0] * (steps * 0.09) + vel[1] = vel[1] * (steps * 0.09) + stars.append(star) + move_stars(stars) + return stars + + +def draw_stars(surface, stars, color): + "used to draw (and clear) the stars" + for vel, pos in stars: + pos = (int(pos[0]), int(pos[1])) + surface.set_at(pos, color) + + +def move_stars(stars): + "animate the star values" + for vel, pos in stars: + pos[0] = pos[0] + vel[0] + pos[1] = pos[1] + vel[1] + if not 0 <= pos[0] <= WINSIZE[0] or not 0 <= pos[1] <= WINSIZE[1]: + vel[:], pos[:] = init_star() + else: + vel[0] = vel[0] * 1.05 + vel[1] = vel[1] * 1.05 + + +def main(): + "This is the starfield code" + # create our starfield + random.seed() + stars = initialize_stars() + clock = pg.time.Clock() + # initialize and prepare screen + pg.init() + screen = pg.display.set_mode(WINSIZE) + pg.display.set_caption("pygame Stars Example") + white = 255, 240, 200 + black = 20, 20, 40 + screen.fill(black) + + # main game loop + done = 0 + while not done: + draw_stars(screen, stars, black) + move_stars(stars) + draw_stars(screen, stars, white) + pg.display.update() + for e in pg.event.get(): + if e.type == pg.QUIT or (e.type == pg.KEYUP and e.key == pg.K_ESCAPE): + done = 1 + break + elif e.type == pg.MOUSEBUTTONDOWN and e.button == 1: + WINCENTER[:] = list(e.pos) + clock.tick(50) + + +# if python says run, then we should run +if __name__ == "__main__": + main() + + # I prefer the time of insects to the time of stars. + # + # -- Wisława Szymborska diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/testsprite.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/testsprite.py new file mode 100644 index 0000000..db8c7be --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/testsprite.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python +""" pg.examples.testsprite + +Like the testsprite.c that comes with libsdl, this pygame version shows +lots of sprites moving around. + +It is an abomination of ugly code, and mostly used for testing. + + +See pg.examples.aliens for some prettyier code. +""" +import sys +import os + +from random import randint +from time import time + +import pygame as pg +from pygame.compat import xrange_ + + +if "-psyco" in sys.argv: + try: + import psyco + + psyco.full() + except Exception: + print("No psyco for you! psyco failed to import and run.") + +main_dir = os.path.split(os.path.abspath(__file__))[0] +data_dir = os.path.join(main_dir, "data") + + +# use this to use update rects or not. +# If the screen is mostly full, then update rects are not useful. +update_rects = True +if "-update_rects" in sys.argv: + update_rects = True +if "-noupdate_rects" in sys.argv: + update_rects = False + +use_static = False +if "-static" in sys.argv: + use_static = True + + +use_layered_dirty = False +if "-layered_dirty" in sys.argv: + update_rects = True + use_layered_dirty = True + + +flags = 0 +if "-flip" in sys.argv: + flags ^= DOUBLEBUF + +if "-fullscreen" in sys.argv: + flags ^= FULLSCREEN + +if "-sw" in sys.argv: + flags ^= SWSURFACE + +use_rle = True + +if "-hw" in sys.argv: + flags ^= HWSURFACE + use_rle = False + +if "-scaled" in sys.argv: + flags ^= SCALED + +screen_dims = [640, 480] + +if "-height" in sys.argv: + i = sys.argv.index("-height") + screen_dims[1] = int(sys.argv[i + 1]) + +if "-width" in sys.argv: + i = sys.argv.index("-width") + screen_dims[0] = int(sys.argv[i + 1]) + +if "-alpha" in sys.argv: + use_alpha = True +else: + use_alpha = False + +print(screen_dims) + + +##class Thingy(pg.sprite.Sprite): +## images = None +## def __init__(self): +## pg.sprite.Sprite.__init__(self) +## self.image = Thingy.images[0] +## self.rect = self.image.get_rect() +## self.rect.x = randint(0, screen_dims[0]) +## self.rect.y = randint(0, screen_dims[1]) +## #self.vel = [randint(-10, 10), randint(-10, 10)] +## self.vel = [randint(-1, 1), randint(-1, 1)] +## +## def move(self): +## for i in [0, 1]: +## nv = self.rect[i] + self.vel[i] +## if nv >= screen_dims[i] or nv < 0: +## self.vel[i] = -self.vel[i] +## nv = self.rect[i] + self.vel[i] +## self.rect[i] = nv + + +class Thingy(pg.sprite.DirtySprite): + images = None + + def __init__(self): + ## pg.sprite.Sprite.__init__(self) + pg.sprite.DirtySprite.__init__(self) + self.image = Thingy.images[0] + self.rect = self.image.get_rect() + self.rect.x = randint(0, screen_dims[0]) + self.rect.y = randint(0, screen_dims[1]) + # self.vel = [randint(-10, 10), randint(-10, 10)] + self.vel = [randint(-1, 1), randint(-1, 1)] + self.dirty = 2 + + def update(self): + for i in [0, 1]: + nv = self.rect[i] + self.vel[i] + if nv >= screen_dims[i] or nv < 0: + self.vel[i] = -self.vel[i] + nv = self.rect[i] + self.vel[i] + self.rect[i] = nv + + +class Static(pg.sprite.DirtySprite): + images = None + + def __init__(self): + pg.sprite.DirtySprite.__init__(self) + self.image = Static.images[0] + self.rect = self.image.get_rect() + self.rect.x = randint(0, 3 * screen_dims[0] / 4) + self.rect.y = randint(0, 3 * screen_dims[1] / 4) + + +def main( + update_rects=True, + use_static=False, + use_layered_dirty=False, + screen_dims=[640, 480], + use_alpha=False, + flags=0, +): + """Show lots of sprites moving around + + Optional keyword arguments: + update_rects - use the RenderUpdate sprite group class (default True) + use_static - include non-moving images (default False) + use_layered_dirty - Use the FastRenderGroup sprite group (default False) + screen_dims - Pygame window dimensions (default [640, 480]) + use_alpha - use alpha blending (default False) + flags - additional display mode flags (default no additional flags) + + """ + + if use_layered_dirty: + update_rects = True + + # pg.init() + pg.display.init() + + # if "-fast" in sys.argv: + + screen = pg.display.set_mode(screen_dims, flags) + + # this is mainly for GP2X, so it can quit. + pg.joystick.init() + num_joysticks = pg.joystick.get_count() + if num_joysticks > 0: + stick = pg.joystick.Joystick(0) + stick.init() # now we will receive events for the joystick + + screen.fill([0, 0, 0]) + pg.display.flip() + sprite_surface = pg.image.load(os.path.join(data_dir, "asprite.bmp")) + sprite_surface2 = pg.image.load(os.path.join(data_dir, "static.png")) + + if use_rle: + sprite_surface.set_colorkey([0xFF, 0xFF, 0xFF], pg.SRCCOLORKEY | pg.RLEACCEL) + sprite_surface2.set_colorkey([0xFF, 0xFF, 0xFF], pg.SRCCOLORKEY | pg.RLEACCEL) + else: + sprite_surface.set_colorkey([0xFF, 0xFF, 0xFF], pg.SRCCOLORKEY) + sprite_surface2.set_colorkey([0xFF, 0xFF, 0xFF], pg.SRCCOLORKEY) + + if use_alpha: + sprite_surface = sprite_surface.convert_alpha() + sprite_surface2 = sprite_surface2.convert_alpha() + else: + sprite_surface = sprite_surface.convert() + sprite_surface2 = sprite_surface2.convert() + + Thingy.images = [sprite_surface] + if use_static: + Static.images = [sprite_surface2] + + if len(sys.argv) > 1: + try: + numsprites = int(sys.argv[-1]) + except Exception: + numsprites = 100 + else: + numsprites = 100 + sprites = None + if use_layered_dirty: + ## sprites = pg.sprite.FastRenderGroup() + sprites = pg.sprite.LayeredDirty() + else: + if update_rects: + sprites = pg.sprite.RenderUpdates() + else: + sprites = pg.sprite.Group() + + for i in xrange_(0, numsprites): + if use_static and i % 2 == 0: + sprites.add(Static()) + sprites.add(Thingy()) + + frames = 0 + start = time() + + background = pg.Surface(screen.get_size()) + background = background.convert() + background.fill([0, 0, 0]) + + going = True + while going: + if not update_rects: + screen.fill([0, 0, 0]) + + ## for sprite in sprites: + ## sprite.move() + + if update_rects: + sprites.clear(screen, background) + sprites.update() + + rects = sprites.draw(screen) + if update_rects: + pg.display.update(rects) + else: + pg.display.flip() + + for event in pg.event.get(): + if event.type in [pg.QUIT, pg.KEYDOWN, pg.QUIT, pg.JOYBUTTONDOWN]: + going = False + + frames += 1 + end = time() + print("FPS: %f" % (frames / ((end - start)))) + pg.quit() + + +if __name__ == "__main__": + main(update_rects, use_static, use_layered_dirty, screen_dims, use_alpha, flags) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/textinput.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/textinput.py new file mode 100644 index 0000000..2219ca7 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/textinput.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +""" pg.examples.textinput + +A little "console" where you can write in text. + +Shows how to use the TEXTEDITING and TEXTINPUT events. +""" +import sys +import pygame as pg +import pygame.freetype as freetype + +# Version check +if pg.get_sdl_version() < (2, 0, 0): + raise Exception("This example requires pygame 2.") + +###CONSTS +# Set to true or add 'showevent' in argv to see IME and KEYDOWN events +PRINT_EVENT = False +# frames per second, the general speed of the program +FPS = 50 +# size of window +WINDOWWIDTH, WINDOWHEIGHT = 640, 480 +BGCOLOR = (0, 0, 0) + +# position of chatlist and chatbox +CHATLIST_POS = pg.Rect(0, 20, WINDOWWIDTH, 400) +CHATBOX_POS = pg.Rect(0, 440, WINDOWWIDTH, 40) +CHATLIST_MAXSIZE = 20 + +TEXTCOLOR = (0, 255, 0) + +# Add fontname for each language, otherwise some text can't be correctly displayed. +FONTNAMES = [ + "notosanscjktcregular", + "notosansmonocjktcregular", + "notosansregular,", + "microsoftjhengheimicrosoftjhengheiuilight", + "microsoftyaheimicrosoftyaheiuilight", + "msgothicmsuigothicmspgothic", + "msmincho", + "Arial", +] + +# Initalize +pg.init() +Screen = pg.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) +pg.display.set_caption("TextInput example") +FPSClock = pg.time.Clock() + +# Freetype +# "The font name can be a comma separated list of font names to search for." +FONTNAMES = ",".join(str(x) for x in FONTNAMES) +Font = freetype.SysFont(FONTNAMES, 24) +FontSmall = freetype.SysFont(FONTNAMES, 16) +print("Using font: " + Font.name) + +# Main loop process +def main(): + global BGCOLOR, PRINT_EVENT, CHATBOX_POS, CHATLIST_POS, CHATLIST_MAXSIZE + global FPSClock, Font, Screen + + """ + https://wiki.libsdl.org/SDL_HINT_IME_INTERNAL_EDITING + https://wiki.libsdl.org/Tutorials/TextInput + Candidate list not showing due to SDL2 problem ;w; + """ + pg.key.start_text_input() + input_rect = pg.Rect(80, 80, 320, 40) + pg.key.set_text_input_rect(input_rect) + + _IMEEditing = False + _IMEText = "" + _IMETextPos = 0 + _IMEEditingText = "" + _IMEEditingPos = 0 + ChatList = [] + + while True: + for event in pg.event.get(): + if event.type == pg.QUIT: + pg.quit() + return + + elif event.type == pg.KEYDOWN: + if PRINT_EVENT: + print(event) + + if _IMEEditing: + if len(_IMEEditingText) == 0: + _IMEEditing = False + continue + + if event.key == pg.K_BACKSPACE: + if len(_IMEText) > 0 and _IMETextPos > 0: + _IMEText = ( + _IMEText[0 : _IMETextPos - 1] + _IMEText[_IMETextPos:] + ) + _IMETextPos = max(0, _IMETextPos - 1) + + elif event.key == pg.K_DELETE: + _IMEText = _IMEText[0:_IMETextPos] + _IMEText[_IMETextPos + 1 :] + elif event.key == pg.K_LEFT: + _IMETextPos = max(0, _IMETextPos - 1) + elif event.key == pg.K_RIGHT: + _IMETextPos = min(len(_IMEText), _IMETextPos + 1) + + elif ( + event.key in [pg.K_RETURN, pg.K_KP_ENTER] + and len(event.unicode) == 0 + ): + # Block if we have no text to append + if len(_IMEText) == 0: + continue + + # Append chat list + ChatList.append(_IMEText) + if len(ChatList) > CHATLIST_MAXSIZE: + ChatList.pop(0) + _IMEText = "" + _IMETextPos = 0 + + elif event.type == pg.TEXTEDITING: + if PRINT_EVENT: + print(event) + _IMEEditing = True + _IMEEditingText = event.text + _IMEEditingPos = event.start + + elif event.type == pg.TEXTINPUT: + if PRINT_EVENT: + print(event) + _IMEEditing = False + _IMEEditingText = "" + _IMEText = _IMEText[0:_IMETextPos] + event.text + _IMEText[_IMETextPos:] + _IMETextPos += len(event.text) + + # Screen updates + Screen.fill(BGCOLOR) + + # Chat List updates + chat_height = CHATLIST_POS.height / CHATLIST_MAXSIZE + for i in range(len(ChatList)): + FontSmall.render_to( + Screen, + (CHATLIST_POS.x, CHATLIST_POS.y + i * chat_height), + ChatList[i], + TEXTCOLOR, + ) + + # Chat box updates + start_pos = CHATBOX_POS.copy() + ime_textL = ">" + _IMEText[0:_IMETextPos] + ime_textM = ( + _IMEEditingText[0:_IMEEditingPos] + "|" + _IMEEditingText[_IMEEditingPos:] + ) + ime_textR = _IMEText[_IMETextPos:] + + rect_textL = Font.render_to(Screen, start_pos, ime_textL, TEXTCOLOR) + start_pos.x += rect_textL.width + + # Editing texts should be underlined + rect_textM = Font.render_to( + Screen, start_pos, ime_textM, TEXTCOLOR, None, freetype.STYLE_UNDERLINE + ) + start_pos.x += rect_textM.width + Font.render_to(Screen, start_pos, ime_textR, TEXTCOLOR) + + pg.display.update() + + FPSClock.tick(FPS) + + +if __name__ == "__main__": + if "showevent" in sys.argv: + PRINT_EVENT = True + + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/vgrade.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/vgrade.py new file mode 100644 index 0000000..5797ac7 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/vgrade.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +""" pg.examples.vgrade + +This example demonstrates creating an image with numpy +python, and displaying that through SDL. You can look at the +method of importing numpy and pg.surfarray. This method +will fail 'gracefully' if it is not available. +I've tried mixing in a lot of comments where the code might +not be self explanatory, nonetheless it may still seem a bit +strange. Learning to use numpy for images like this takes a +bit of learning, but the payoff is extremely fast image +manipulation in python. + +For Pygame 1.9.2 and up, this example also showcases a new feature +of surfarray.blit_surface: array broadcasting. If a source array +has either a width or height of 1, the array is repeatedly blitted +to the surface along that dimension to fill the surface. In fact, +a (1, 1) or (1, 1, 3) array results in a simple surface color fill. + +Just so you know how this breaks down. For each sampling of +time, 30% goes to each creating the gradient and blitting the +array. The final 40% goes to flipping/updating the display surface + +The window will have no border decorations. + +The code also demonstrates use of the timer events. +""" + + +import os +import pygame as pg + +try: + import numpy as np + import numpy.random as np_random +except ImportError: + raise SystemExit("This example requires numpy and the pygame surfarray module") + +timer = 0 + + +def stopwatch(message=None): + "simple routine to time python code" + global timer + if not message: + timer = pg.time.get_ticks() + return + now = pg.time.get_ticks() + runtime = (now - timer) / 1000.0 + 0.001 + print("%s %s %s" % (message, runtime, ("seconds\t(%.2ffps)" % (1.0 / runtime)))) + timer = now + + +def VertGradientColumn(surf, topcolor, bottomcolor): + "creates a new 3d vertical gradient array" + topcolor = np.array(topcolor, copy=False) + bottomcolor = np.array(bottomcolor, copy=False) + diff = bottomcolor - topcolor + width, height = surf.get_size() + # create array from 0.0 to 1.0 triplets + column = np.arange(height, dtype="float") / height + column = np.repeat(column[:, np.newaxis], [3], 1) + # create a single column of gradient + column = topcolor + (diff * column).astype("int") + # make the column a 3d image column by adding X + column = column.astype("uint8")[np.newaxis, :, :] + # 3d array into 2d array + return pg.surfarray.map_array(surf, column) + + +def DisplayGradient(surf): + "choose random colors and show them" + stopwatch() + colors = np_random.randint(0, 255, (2, 3)) + column = VertGradientColumn(surf, colors[0], colors[1]) + pg.surfarray.blit_array(surf, column) + pg.display.flip() + stopwatch("Gradient:") + + +def main(): + pg.init() + pg.mixer.quit() # remove ALSA underflow messages for Debian squeeze + size = 600, 400 + os.environ["SDL_VIDEO_CENTERED"] = "1" + screen = pg.display.set_mode(size, pg.NOFRAME, 0) + + pg.event.set_blocked(pg.MOUSEMOTION) # keep our queue cleaner + pg.time.set_timer(pg.USEREVENT, 500) + + while 1: + event = pg.event.wait() + if event.type in (pg.QUIT, pg.KEYDOWN, pg.MOUSEBUTTONDOWN): + break + elif event.type == pg.USEREVENT: + DisplayGradient(screen) + + +if __name__ == "__main__": + main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/examples/video.py b/Display/.venv/lib/python3.7/site-packages/pygame/examples/video.py new file mode 100644 index 0000000..fdc3215 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/examples/video.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python +""" pg.examples.video + +Experimental! + +* dialog message boxes with messagebox. +* multiple windows with Window +* driver selection +* Renderer, Texture, and Image classes +* Drawing lines, rects, and such onto Renderers. +""" +import os +import pygame as pg + +if pg.get_sdl_version()[0] < 2: + raise SystemExit( + "This example requires pygame 2 and SDL2. _sdl2 is experimental and will change." + ) +from pygame._sdl2 import Window, Texture, Image, Renderer, get_drivers, messagebox + +data_dir = os.path.join(os.path.split(os.path.abspath(__file__))[0], "data") + + +def load_img(file): + return pg.image.load(os.path.join(data_dir, file)) + + +pg.display.init() +pg.key.set_repeat(1000, 10) + +for driver in get_drivers(): + print(driver) + +import random + +answer = messagebox( + "I will open two windows! Continue?", + "Hello!", + info=True, + buttons=("Yes", "No", "Chance"), + return_button=0, + escape_button=1, +) +if answer == 1 or (answer == 2 and random.random() < 0.5): + import sys + + sys.exit(0) + +win = Window("asdf", resizable=True) +renderer = Renderer(win) +tex = Texture.from_surface(renderer, load_img("alien1.gif")) + +running = True + +x, y = 250, 50 +clock = pg.time.Clock() + +backgrounds = [(255, 0, 0, 255), (0, 255, 0, 255), (0, 0, 255, 255)] +bg_index = 0 + +renderer.draw_color = backgrounds[bg_index] + +win2 = Window("2nd window", size=(256, 256), always_on_top=True) +win2.opacity = 0.5 +win2.set_icon(load_img("bomb.gif")) +renderer2 = Renderer(win2) +tex2 = Texture.from_surface(renderer2, load_img("asprite.bmp")) +renderer2.clear() +tex2.draw() +renderer2.present() +del tex2 + +full = 0 + +tex = Image(tex) + + +surf = pg.Surface((64, 64)) +streamtex = Texture(renderer, (64, 64), streaming=True) +tex_update_interval = 1000 +next_tex_update = pg.time.get_ticks() + + +while running: + for event in pg.event.get(): + if event.type == pg.QUIT: + running = False + elif getattr(event, "window", None) == win2: + if ( + event.type == pg.KEYDOWN + and event.key == pg.K_ESCAPE + or event.type == pg.WINDOWEVENT + and event.event == pg.WINDOWEVENT_CLOSE + ): + win2.destroy() + elif event.type == pg.KEYDOWN: + if event.key == pg.K_ESCAPE: + running = False + elif event.key == pg.K_LEFT: + x -= 5 + elif event.key == pg.K_RIGHT: + x += 5 + elif event.key == pg.K_DOWN: + y += 5 + elif event.key == pg.K_UP: + y -= 5 + elif event.key == pg.K_f: + if full == 0: + win.set_fullscreen(True) + full = 1 + else: + win.set_windowed() + full = 0 + elif event.key == pg.K_s: + readsurf = renderer.to_surface() + pg.image.save(readsurf, "test.png") + + elif event.key == pg.K_SPACE: + bg_index = (bg_index + 1) % len(backgrounds) + renderer.draw_color = backgrounds[bg_index] + + renderer.clear() + + # update texture + curtime = pg.time.get_ticks() + if curtime >= next_tex_update: + for x_ in range(streamtex.width // 4): + for y_ in range(streamtex.height // 4): + newcol = ( + random.randint(0, 255), + random.randint(0, 255), + random.randint(0, 255), + 255, + ) + area = (4 * x_, 4 * y_, 4, 4) + surf.fill(newcol, area) + streamtex.update(surf) + next_tex_update = curtime + tex_update_interval + streamtex.draw(dstrect=pg.Rect(64, 128, 64, 64)) + + tex.draw(dstrect=(x, y)) + + # TODO: should these be? + # - line instead of draw_line + # - point instead of draw_point + # - rect(rect, width=1)->draw 1 pixel, instead of draw_rect + # - rect(rect, width=0)->filled ? , instead of fill_rect + # + # TODO: should these work with pg.draw.line(renderer, ...) functions? + renderer.draw_color = (255, 255, 255, 255) + renderer.draw_line((0, 0), (64, 64)) + renderer.draw_line((64, 64), (128, 0)) + renderer.draw_point((72, 32)) + renderer.draw_rect(pg.Rect(0, 64, 64, 64)) + renderer.fill_rect(pg.Rect(0, 128, 64, 64)) + renderer.draw_color = backgrounds[bg_index] + + renderer.present() + + clock.tick(60) + win.title = str("FPS: {}".format(clock.get_fps())) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/fastevent.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/fastevent.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..900821d Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/fastevent.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/fastevent.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/fastevent.pyi new file mode 100644 index 0000000..2740816 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/fastevent.pyi @@ -0,0 +1,10 @@ +from typing import List +from pygame.event import EventType + +def init() -> None: ... +def get_init() -> bool: ... +def pump() -> None: ... +def wait() -> EventType: ... +def pool() -> EventType: ... +def get() -> List[EventType]: ... +def post(event: EventType) -> None: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/font.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/font.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..449de12 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/font.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/font.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/font.pyi new file mode 100644 index 0000000..deb5dd6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/font.pyi @@ -0,0 +1,42 @@ +from typing import List, Optional, Union, Tuple + +from pygame.color import Color +from pygame.surface import Surface + +_ColorValue = Union[ + Color, Tuple[int, int, int], List[int], int, Tuple[int, int, int, int] +] + +def init() -> None: ... +def quit() -> None: ... +def get_init() -> bool: ... +def get_default_font() -> str: ... +def get_fonts() -> List[str]: ... +def match_font( + name: str, bold: Optional[bool] = False, italic: Optional[bool] = False +) -> str: ... +def SysFont( + name: str, size: int, bold: Optional[bool] = False, italic: Optional[bool] = False +) -> Font: ... + +class Font(object): + def __init__(self, name: Union[str, None], size: int) -> None: ... + def render( + self, + text: str, + antialias: bool, + color: _ColorValue, + background: Optional[_ColorValue] = None, + ) -> Surface: ... + def size(self, text: str) -> Tuple[int, int]: ... + def set_underline(self, value: bool) -> None: ... + def get_underline(self) -> bool: ... + def set_bold(self, value: bool) -> None: ... + def get_bold(self) -> bool: ... + def set_italic(self, value: bool) -> None: ... + def metrics(self, text: str) -> List[Tuple[int, int, int, int, int]]: ... + def get_italic(self) -> bool: ... + def get_linesize(self) -> int: ... + def get_height(self) -> int: ... + def get_ascent(self) -> int: ... + def get_descent(self) -> int: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/freesansbold.ttf b/Display/.venv/lib/python3.7/site-packages/pygame/freesansbold.ttf new file mode 100644 index 0000000..a98562f Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/freesansbold.ttf differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/freetype.py b/Display/.venv/lib/python3.7/site-packages/pygame/freetype.py new file mode 100644 index 0000000..bb531de --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/freetype.py @@ -0,0 +1,49 @@ +"""Enhanced Pygame module for loading and rendering computer fonts""" + +from pygame._freetype import ( + Font, + STYLE_NORMAL, STYLE_OBLIQUE, STYLE_STRONG, STYLE_UNDERLINE, STYLE_WIDE, + STYLE_DEFAULT, + init, quit, get_init, + was_init, get_cache_size, get_default_font, get_default_resolution, + get_error, get_version, set_default_resolution, + _PYGAME_C_API, __PYGAMEinit__, + ) +from pygame.sysfont import match_font, get_fonts, SysFont as _SysFont + +__all__ = ["Font", "STYLE_NORMAL", "STYLE_OBLIQUE", "STYLE_STRONG", + "STYLE_UNDERLINE", "STYLE_WIDE", "STYLE_DEFAULT", "init", "quit", + "get_init", "was_init", "get_cache_size", "get_default_font", + "get_default_resolution", "get_error", "get_version", + "set_default_resolution", "match_font", "get_fonts"] + +def SysFont(name, size, bold=0, italic=0, constructor=None): + """pygame.ftfont.SysFont(name, size, bold=False, italic=False, constructor=None) -> Font + create a pygame Font from system font resources + + This will search the system fonts for the given font + name. You can also enable bold or italic styles, and + the appropriate system font will be selected if available. + + This will always return a valid Font object, and will + fallback on the builtin pygame font if the given font + is not found. + + Name can also be a comma separated list of names, in + which case set of names will be searched in order. Pygame + uses a small set of common font aliases, if the specific + font you ask for is not available, a reasonable alternative + may be used. + + if optional constructor is provided, it must be a function with + signature constructor(fontpath, size, bold, italic) which returns + a Font instance. If None, a pygame.freetype.Font object is created. + """ + if constructor is None: + def constructor(fontpath, size, bold, italic): + font = Font(fontpath, size) + font.strong = bold + font.oblique = italic + return font + + return _SysFont(name, size, bold, italic, constructor) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/freetype.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/freetype.pyi new file mode 100644 index 0000000..50c3a2e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/freetype.pyi @@ -0,0 +1,120 @@ +from typing import Tuple, Optional, Union, List, Text, IO, Sequence, Any + +from pygame.surface import Surface +from pygame.color import Color +from pygame.rect import Rect + +_ColorValue = Union[Color, Tuple[int, int, int], List[int], int] + +def get_error() -> str: ... +def get_version() -> Tuple[int, int, int]: ... +def init(cache_size: Optional[int] = 64, resolution: Optional[int] = 72): ... +def quit(): ... +def get_init() -> bool: ... +def was_init() -> bool: ... +def get_cache_size() -> int: ... +def get_default_resolution() -> int: ... +def set_default_resolution(resolution: int) -> None: ... +def SysFont( + name: Union[str, List[str]], + size: int, + bold: Optional[bool] = False, + italic: Optional[bool] = False, +): ... +def get_default_font() -> str: ... + +STYLE_NORMAL: int +STYLE_UNDERLINE: int +STYLE_OBLIQUE: int +STYLE_STRONG: int +STYLE_WIDE: int +STYLE_DEFAULT: int + +class Font: + name: str + path: Text + size: Union[float, Tuple[float, float]] + height: int + ascender: int + descender: int + style: int + underline: bool + strong: bool + oblique: bool + wide: bool + strength: float + underline_adjustment: float + fixed_width: bool + fixed_sizes: int + scalable: bool + use_bitmap_strikes: bool + antialiased: bool + kerning: bool + vertical: bool + rotation: int + fgcolor: Color + origin: bool + pad: bool + ucs4: bool + resolution: int + def __init__( + self, + file: Union[str, IO], + size: Optional[float] = 0, + font_index: Optional[int] = 0, + resolution: Optional[int] = 0, + ucs4: Optional[int] = False, + ) -> None: ... + def get_rect( + self, + text: str, + style: Optional[int] = STYLE_DEFAULT, + rotation: Optional[int] = 0, + size: Optional[float] = 0, + ) -> Rect: ... + def get_metrics( + self, text: str, size: Optional[float] = 0 + ) -> List[Tuple[int, int, int, int, float, float]]: ... + def get_sized_ascender(self, size: float) -> int: ... + def get_sized_descender(self, size: float) -> int: ... + def get_sized_height(self, size: float) -> int: ... + def get_sized_glyph_height(self, size: float) -> int: ... + def get_sizes(self) -> List[Tuple[int, int, int, float, float]]: ... + def render( + self, + text: str, + fgcolor: Optional[_ColorValue] = None, + bgcolor: Optional[_ColorValue] = None, + style: Optional[int] = STYLE_DEFAULT, + rotation: Optional[int] = 0, + size: Optional[float] = 0, + ) -> Tuple[Surface, Rect]: ... + def render_to( + self, + surf: Surface, + dest: Union[Tuple[int, int], Sequence[int], Rect], + text: str, + fgcolor: Optional[_ColorValue] = None, + bgcolor: Optional[_ColorValue] = None, + style: Optional[int] = STYLE_DEFAULT, + rotation: Optional[int] = 0, + size: Optional[float] = 0, + ) -> Rect: ... + def render_raw( + self, + text: str, + style: Optional[int] = STYLE_DEFAULT, + rotation: Optional[int] = 0, + size: Optional[float] = 0, + invert: Optional[bool] = False, + ) -> Tuple[bytes, Tuple[int, int]]: ... + def render_raw_to( + self, + array: Any, + text: str, + dest: Optional[Union[Tuple[int, int], List[int]]] = None, + style: Optional[int] = STYLE_DEFAULT, + rotation: Optional[int] = 0, + size: Optional[float] = 0, + invert: Optional[bool] = False, + ) -> Rect: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/ftfont.py b/Display/.venv/lib/python3.7/site-packages/pygame/ftfont.py new file mode 100644 index 0000000..ea7a14b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/ftfont.py @@ -0,0 +1,188 @@ +"""pygame module for loading and rendering fonts (freetype alternative)""" + +__all__ = ['Font', 'init', 'quit', 'get_default_font', 'get_init', 'SysFont', + "match_font", "get_fonts"] + +from pygame._freetype import init, Font as _Font, get_default_resolution +from pygame._freetype import quit, get_default_font, get_init as _get_init +from pygame._freetype import __PYGAMEinit__ +from pygame.sysfont import match_font, get_fonts, SysFont as _SysFont +from pygame import encode_file_path +from pygame.compat import bytes_, unicode_, as_unicode, as_bytes + + +class Font(_Font): + """Font(filename, size) -> Font + Font(object, size) -> Font + create a new Font object from a file (freetype alternative) + + This Font type differs from font.Font in that it can render glyphs + for Unicode code points in the supplementary planes (> 0xFFFF). + """ + + __encode_file_path = staticmethod(encode_file_path) + __get_default_resolution = staticmethod(get_default_resolution) + __default_font = encode_file_path(get_default_font()) + + __unull = as_unicode(r"\x00") + __bnull = as_bytes("\x00") + + def __init__(self, file, size=-1): + if size <= 1: + size = 1 + if isinstance(file, unicode_): + try: + bfile = self.__encode_file_path(file, ValueError) + except ValueError: + bfile = '' + else: + bfile = file + if isinstance(bfile, bytes_) and bfile == self.__default_font: + file = None + if file is None: + resolution = int(self.__get_default_resolution() * 0.6875) + if resolution == 0: + kwds['resolution'] = 1 + else: + resolution = 0 + super(Font, self).__init__(file, size=size, resolution=resolution) + self.strength = 1.0 / 12.0 + self.kerning = False + self.origin = True + self.pad = True + self.ucs4 = True + self.underline_adjustment = 1.0 + + def render(self, text, antialias, color, background=None): + """render(text, antialias, color, background=None) -> Surface + draw text on a new Surface""" + + if text is None: + text = "" + if (isinstance(text, unicode_) and # conditional and + self.__unull in text): + raise ValueError("A null character was found in the text") + if (isinstance(text, bytes_) and # conditional and + self.__bnull in text): + raise ValueError("A null character was found in the text") + save_antialiased = self.antialiased + self.antialiased = bool(antialias) + try: + s, r = super(Font, self).render(text, color, background) + return s + finally: + self.antialiased = save_antialiased + + def set_bold(self, value): + """set_bold(bool) -> None + enable fake rendering of bold text""" + + self.wide = bool(value) + + def get_bold(self): + """get_bold() -> bool + check if text will be rendered bold""" + + return self.wide + + def set_italic(self, value): + """set_italic(bool) -> None + enable fake rendering of italic text""" + + self.oblique = bool(value) + + def get_italic(self): + """get_italic() -> bool + check if the text will be rendered italic""" + + return self.oblique + + def set_underline(self, value): + """set_underline(bool) -> None + control if text is rendered with an underline""" + + self.underline = bool(value) + + def get_underline(self): + """set_bold(bool) -> None + enable fake rendering of bold text""" + + return self.underline + + def metrics(self, text): + """metrics(text) -> list + Gets the metrics for each character in the passed string.""" + + return self.get_metrics(text) + + def get_ascent(self): + """get_ascent() -> int + get the ascent of the font""" + + return self.get_sized_ascender() + + def get_descent(self): + """get_descent() -> int + get the descent of the font""" + + return self.get_sized_descender() + + def get_height(self): + """get_height() -> int + get the height of the font""" + + return self.get_sized_ascender() - self.get_sized_descender() + 1 + + def get_linesize(self): + """get_linesize() -> int + get the line space of the font text""" + + return self.get_sized_height(); + + def size(self, text): + """size(text) -> (width, height) + determine the amount of space needed to render text""" + + return self.get_rect(text).size + +FontType = Font + +def get_init(): + """get_init() -> bool + true if the font module is initialized""" + + return _get_init() + +def SysFont(name, size, bold=0, italic=0, constructor=None): + """pygame.ftfont.SysFont(name, size, bold=False, italic=False, constructor=None) -> Font + create a pygame Font from system font resources (freetype alternative) + + This will search the system fonts for the given font + name. You can also enable bold or italic styles, and + the appropriate system font will be selected if available. + + This will always return a valid Font object, and will + fallback on the builtin pygame font if the given font + is not found. + + Name can also be a comma separated list of names, in + which case set of names will be searched in order. Pygame + uses a small set of common font aliases, if the specific + font you ask for is not available, a reasonable alternative + may be used. + + if optional constructor is provided, it must be a function with + signature constructor(fontpath, size, bold, italic) which returns + a Font instance. If None, a pygame.ftfont.Font object is created. + """ + if constructor is None: + def constructor(fontpath, size, bold, italic): + font = Font(fontpath, size) + font.set_bold(bold) + font.set_italic(italic) + return font + + return _SysFont(name, size, bold, italic, constructor) + +del _Font, get_default_resolution, encode_file_path, as_unicode, as_bytes + diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/gfxdraw.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/gfxdraw.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..ade76d3 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/gfxdraw.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/gfxdraw.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/gfxdraw.pyi new file mode 100644 index 0000000..ae42058 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/gfxdraw.pyi @@ -0,0 +1,98 @@ +from typing import Union, Tuple, List, Sequence + +from pygame.surface import Surface +from pygame.math import Vector2 +from pygame.color import Color +from pygame.rect import Rect + +_ColorValue = Union[ + Color, Tuple[int, int, int], List[int], int, Tuple[int, int, int, int] +] +_RectValue = Union[Rect, Tuple[int, int, int, int]] +_Coordinate = Union[Tuple[float, float], List[float], Vector2] + +def pixel(surface: Surface, x: int, y: int, color: _ColorValue) -> None: ... +def hline(surface: Surface, x1: int, x2: int, y: int, color: _ColorValue) -> None: ... +def vline(surface: Surface, x: int, y1: int, y2: int, color: _ColorValue) -> None: ... +def line( + surface: Surface, x1: int, y1: int, x2: int, y2: int, color: _ColorValue +) -> None: ... +def rectangle(surface: Surface, rect: _RectValue, color: _ColorValue) -> None: ... +def box(surface: Surface, rect: _RectValue, color: _ColorValue) -> None: ... +def circle(surface: Surface, x: int, y: int, r: int, color: _ColorValue) -> None: ... +def aacircle(surface: Surface, x: int, y: int, r: int, color: _ColorValue) -> None: ... +def filled_circle( + surface: Surface, x: int, y: int, r: int, color: _ColorValue +) -> None: ... +def ellipse( + surface: Surface, x: int, y: int, rx: int, ry: int, color: _ColorValue +) -> None: ... +def aaellipse( + surface: Surface, x: int, y: int, rx: int, ry: int, color: _ColorValue +) -> None: ... +def filled_ellipse( + surface: Surface, x: int, y: int, rx: int, ry: int, color: _ColorValue +) -> None: ... +def arc( + surface: Surface, + x: int, + y: int, + r: int, + start_angle: int, + atp_angle: int, + color: _ColorValue, +) -> None: ... +def pie( + surface: Surface, + x: int, + y: int, + r: int, + start_angle: int, + atp_angle: int, + color: _ColorValue, +) -> None: ... +def trigon( + surface: Surface, + x1: int, + y1: int, + x2: int, + y2: int, + x3: int, + y3: int, + color: _ColorValue, +) -> None: ... +def aatrigon( + surface: Surface, + x1: int, + y1: int, + x2: int, + y2: int, + x3: int, + y3: int, + color: _ColorValue, +) -> None: ... +def filled_trigon( + surface: Surface, + x1: int, + y1: int, + x2: int, + y2: int, + x3: int, + y3: int, + color: _ColorValue, +) -> None: ... +def polygon( + surface: Surface, points: Sequence[_Coordinate], color: _ColorValue +) -> None: ... +def aapolygon( + surface: Surface, points: Sequence[_Coordinate], color: _ColorValue +) -> None: ... +def filled_polygon( + surface: Surface, points: Sequence[_Coordinate], color: _ColorValue +) -> None: ... +def textured_polygon( + surface: Surface, points: Sequence[_Coordinate], texture: Surface, tx: int, ty: int +) -> None: ... +def bezier( + surface: Surface, points: Sequence[_Coordinate], steps: int, color: _ColorValue +) -> None: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/image.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/image.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..151c08f Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/image.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/image.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/image.pyi new file mode 100644 index 0000000..79a906d --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/image.pyi @@ -0,0 +1,17 @@ +from typing import Optional, Tuple, List, Union + +from pygame.surface import Surface + +def load(filename: str, namehint: Optional[str] = "") -> Surface: ... +def save(surface: Surface, filename: str) -> None: ... +def get_extended() -> bool: ... +def tostring(surface: Surface, format: str, flipped: Optional[bool] = False) -> str: ... +def fromstring( + string: str, + size: Union[List[int], Tuple[int, int]], + format: str, + flipped: Optional[bool] = False, +) -> Surface: ... +def frombuffer( + string: str, size: Union[List[int], Tuple[int, int]], format: str +) -> Surface: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/imageext.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/imageext.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..392fbc6 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/imageext.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/joystick.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/joystick.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..60cb5a3 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/joystick.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/joystick.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/joystick.pyi new file mode 100644 index 0000000..64ee42e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/joystick.pyi @@ -0,0 +1,22 @@ +from typing import Tuple + +def init() -> None: ... +def quit() -> None: ... +def get_init() -> bool: ... +def get_count() -> int: ... + +class Joystick(object): + def __init__(self, id: int) -> None: ... + def init(self) -> None: ... + def quit(self) -> None: ... + def get_init(self) -> bool: ... + def get_id(self) -> int: ... + def get_name(self) -> str: ... + def get_numaxes(self) -> int: ... + def get_axis(self, axis_number: int) -> float: ... + def get_numballs(self) -> int: ... + def get_ball(self, ball_number: int) -> Tuple[float, float]: ... + def get_numbuttons(self) -> int: ... + def get_button(self, button: int) -> bool: ... + def get_numhats(self) -> int: ... + def get_hat(self, hat_number: int) -> Tuple[float, float]: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/key.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/key.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..7e537a2 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/key.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/key.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/key.pyi new file mode 100644 index 0000000..e3a634a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/key.pyi @@ -0,0 +1,22 @@ +from typing import Sequence, Optional, Tuple, Union, List +from pygame.math import Vector2 +from pygame.rect import Rect + +_Coordinate = Union[Tuple[float, float], List[float], Vector2] +_RectValue = Union[ + Rect, + Union[Tuple[int, int, int, int], List[int]], + Union[Tuple[_Coordinate, _Coordinate], List[_Coordinate]], +] + +def get_focused() -> bool: ... +def get_pressed() -> Sequence[bool]: ... +def get_mods() -> int: ... +def set_mods() -> int: ... +def set_repeat(delay: Optional[int], interval: Optional[int]) -> None: ... +def get_repeat() -> Tuple[int, int]: ... +def name(key: int) -> str: ... +def key_code(name: str) -> int: ... +def start_text_input() -> None: ... +def stop_text_input() -> None: ... +def set_text_input_rect(_RectValue) -> None: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/locals.py b/Display/.venv/lib/python3.7/site-packages/pygame/locals.py new file mode 100644 index 0000000..9b1f2fb --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/locals.py @@ -0,0 +1,30 @@ +## pygame - Python Game Library +## Copyright (C) 2000-2003 Pete Shinners +## +## This library is free software; you can redistribute it and/or +## modify it under the terms of the GNU Library General Public +## License as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## Library General Public License for more details. +## +## You should have received a copy of the GNU Library General Public +## License along with this library; if not, write to the Free +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## +## Pete Shinners +## pete@shinners.org + + + +"""Set of functions from PyGame that are handy to have in +the local namespace for your module""" + +from pygame.constants import * +from pygame.rect import Rect +import pygame.color as color +Color = color.Color + diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/macosx.py b/Display/.venv/lib/python3.7/site-packages/pygame/macosx.py new file mode 100644 index 0000000..3c79856 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/macosx.py @@ -0,0 +1,22 @@ +import platform +import os +import sys +from pygame.pkgdata import getResource +from pygame import sdlmain_osx + +__all__ = ['Video_AutoInit'] + +def Video_AutoInit(): + """Called from the base.c just before display module is initialized.""" + if 'Darwin' in platform.platform(): + if not sdlmain_osx.RunningFromBundleWithNSApplication(): + default_icon_data = None + try: + with getResource('pygame_icon.tiff') as file_resource: + default_icon_data = file_resource.read() + except (IOError, NotImplementedError): + pass + sdlmain_osx.InstallNSApplication(default_icon_data) + if (os.getcwd() == '/') and len(sys.argv) > 1: + os.chdir(os.path.dirname(sys.argv[0])) + return True diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/mask.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/mask.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..e66c00f Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/mask.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/mask.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/mask.pyi new file mode 100644 index 0000000..eab0ce4 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/mask.pyi @@ -0,0 +1,73 @@ +from typing import Optional, Union, Tuple, List, TypeVar, Sequence, NewType + +from pygame.math import Vector2 +from pygame.surface import Surface +from pygame.rect import Rect +from pygame.color import Color + +_Coordinate = Union[Tuple[float, float], List[float], Vector2] +_ColorValue = Union[ + Color, Tuple[int, int, int], List[int], int, Tuple[int, int, int, int] +] +_RectValue = Union[ + Rect, + Union[Tuple[int, int, int, int], List[int]], + Union[Tuple[_Coordinate, _Coordinate], List[_Coordinate]], +] +_Offset = TypeVar("_Offset", Tuple[int, int], Sequence[int]) + +def from_surface(surface: Surface, threshold: Optional[int] = 127) -> Mask: ... +def from_threshold( + surface: Surface, + color: _ColorValue, + threshold: Optional[_ColorValue] = (0, 0, 0, 255), + other_surface: Optional[Surface] = None, + palette_colors: Optional[int] = 1, +) -> Mask: ... + +class Mask: + def __init__( + self, size: Union[List[int], Tuple[int, int]], fiil: Optional[bool] = False + ) -> None: ... + def copy(self) -> Mask: ... + def get_size(self) -> Tuple[int, int]: ... + def get_rect(self, **kwargs) -> Rect: ... # Dict type needs to be completed + def get_at(self, pos: Union[List[int], Tuple[int, int]]) -> int: ... + def set_at( + self, pos: Union[List[int], Tuple[int, int]], value: Optional[int] = 1 + ) -> None: ... + def overlap( + self, othermask: Mask, offset: _Offset + ) -> Union[Tuple[int, int], None]: ... + def overlap_area(self, othermask: Mask, offset: _Offset) -> int: ... + def overlap_mask(self, othermask: Mask, offset: _Offset) -> Mask: ... + def fill(self) -> None: ... + def clear(self) -> None: ... + def invert(self) -> None: ... + def scale(self, size: Union[List[int], Tuple[int, int]]) -> Mask: ... + def draw(self, othermask: Mask, offset: _Offset) -> None: ... + def erase(self, othermask: Mask, offset: _Offset) -> None: ... + def count(self) -> int: ... + def centroid(self) -> Tuple[int, int]: ... + def angle(self) -> float: ... + def outline(self, every: Optional[int] = 1) -> List[Tuple[int, int]]: ... + def convolve( + self, + othermask: Mask, + outputmask: Optional[Mask] = None, + offset: Optional[_Offset] = (0, 0), + ) -> Mask: ... + def connected_component( + self, pos: Union[List[int], Tuple[int, int]] = (-1, -1) + ) -> Mask: ... + def connected_components(self, min: Optional[int] = 0) -> List[Mask]: ... + def get_bounding_rects(self) -> Rect: ... + def to_surface( + self, + surface: Optional[Surface] = None, + setsurface: Optional[Surface] = None, + unsetsurface: Optional[Surface] = None, + setcolor: Optional[_ColorValue] = (255, 255, 255, 255), + unsetcolor: Optional[_ColorValue] = (0, 0, 0, 255), + dest: Optional[Union[_RectValue, _Coordinate]] = (0, 0), + ) -> Surface: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/math.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/math.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..1e8c2bf Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/math.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/math.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/math.pyi new file mode 100644 index 0000000..22846c9 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/math.pyi @@ -0,0 +1,105 @@ +from typing import Optional, Union, Tuple, List, overload, Sequence + +class VectorElementwiseProxy: ... + +def enable_swizzling() -> None: ... +def disable_swizzling() -> None: ... + +class Vector2: + x: float + y: float + def __init__( + self, + x: Optional[Union[float, Vector2, Tuple[float, float], List[float]]] = 0, + y: Optional[float] = 0, + ) -> None: ... + def dot(self, other: Vector2) -> float: ... + def cross(self, other: Vector2) -> Vector2: ... + def magnitude(self) -> float: ... + def magnitude_squared(self) -> float: ... + def length(self) -> float: ... + def length_squared(self) -> float: ... + def normalize(self) -> Vector2: ... + def normalize_ip(self) -> None: ... + def is_normalized(self) -> bool: ... + def scale_to_length(self, value: float) -> None: ... + def reflect(self, other: Vector2) -> Vector2: ... + def reflect_ip(self, other: Vector2) -> None: ... + def distance_to(self, other: Union[Vector2, Sequence[float]]) -> float: ... + def distance_squared_to(self, other: Vector2) -> float: ... + def lerp(self, other: Vector2, value: float) -> Vector2: ... + def slerp(self, other: Vector2, value: float) -> Vector2: ... + def elementwise(self) -> VectorElementwiseProxy: ... + def rotate(self, angle: float) -> Vector2: ... + def rotate_rad(self, angle: float) -> Vector2: ... + def rotate_ip(self, angle: float) -> None: ... + def rotate_ip_rad(self, angle: float) -> None: ... + def angle_to(self, other: Vector2) -> float: ... + def as_polar(self) -> Tuple[float, float]: ... + def from_polar( + self, polar_value: Union[List[float], Tuple[float, float]] + ) -> None: ... + def update( + self, + x: Optional[Union[float, Vector2, Tuple[float, float], List[float]]] = 0, + y: Optional[float] = 0, + ) -> None: ... + +class Vector3: + x: float + y: float + z: float + @overload + def __init__( + self, + xyz: Optional[ + Union[float, Tuple[float, float, float], List[float], Vector3] + ] = 0, + ) -> None: ... + @overload + def __init__(self, x: int, y: int, z) -> None: ... + def dot(self, other: Vector3) -> float: ... + def cross(self, other: Vector3) -> Vector3: ... + def magnitude(self) -> float: ... + def magnitude_squared(self) -> float: ... + def length(self) -> float: ... + def length_squared(self) -> float: ... + def normalize(self) -> Vector3: ... + def normalize_ip(self) -> None: ... + def is_normalized(self) -> bool: ... + def scale_to_length(self, value: float) -> None: ... + def reflect(self, other: Vector3) -> Vector3: ... + def reflect_ip(self, other: Vector3) -> None: ... + def distance_to(self, other: Vector3) -> float: ... + def distance_squared_to(self, other: Vector3) -> float: ... + def lerp(self, other: Vector3, value: float) -> Vector3: ... + def slerp(self, other: Vector3, value: float) -> Vector3: ... + def elementwise(self) -> VectorElementwiseProxy: ... + def rotate(self, angle: float, axis: Vector3) -> Vector3: ... + def rotate_rad(self, angle: float, axis: Vector3) -> Vector3: ... + def rotate_ip(self, angle: float, axis: Vector3) -> None: ... + def rotate_ip_rad(self, angle: float, axis: Vector3) -> None: ... + def rotate_x(self, angle: float) -> Vector3: ... + def rotate_x_rad(self, angle: float) -> Vector3: ... + def rotate_x_ip(self, angle: float) -> None: ... + def rotate_x_ip_rad(self, angle: float) -> None: ... + def rotate_y(self, angle: float) -> Vector3: ... + def rotate_y_rad(self, angle: float) -> Vector3: ... + def rotate_y_ip(self, angle: float) -> None: ... + def rotate_y_ip_rad(self, angle: float) -> None: ... + def rotate_z(self, angle: float) -> Vector3: ... + def rotate_z_rad(self, angle: float) -> Vector3: ... + def rotate_z_ip(self, angle: float) -> None: ... + def rotate_z_ip_rad(self, angle: float) -> None: ... + def angle_to(self, other: Vector3) -> float: ... + def as_spherical(self) -> Tuple[float, float, float]: ... + def from_spherical(self, spherical: Tuple[float, float, float]) -> None: ... + @overload + def update( + self, + xyz: Optional[ + Union[float, Tuple[float, float, float], List[float], Vector3] + ] = 0, + ) -> None: ... + @overload + def update(self, x: int, y: int, z) -> None: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/midi.py b/Display/.venv/lib/python3.7/site-packages/pygame/midi.py new file mode 100644 index 0000000..f3f3bd2 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/midi.py @@ -0,0 +1,731 @@ +"""pygame.midi +pygame module for interacting with midi input and output. + +The midi module can send output to midi devices, and get input +from midi devices. It can also list midi devices on the system. + +Including real midi devices, and virtual ones. + +It uses the portmidi library. Is portable to which ever platforms +portmidi supports (currently windows, OSX, and linux). + +This uses pyportmidi for now, but may use its own bindings at some +point in the future. The pyportmidi bindings are included with pygame. + +New in pygame 1.9.0. +""" + + +#TODO: +# - finish writing tests. +# - likely as interactive tests... so you'd need to plug in a midi device. +# - create a background thread version for input threads. +# - that can automatically inject input into the event queue +# once the input object is running. Like joysticks. + + +# In Python 2.7 pygame.math is imported instead of the built-in math module. +# This import from future allows the built-in math module to be imported. +from __future__ import absolute_import +import math +import atexit + +import pygame +import pygame.locals + + +# For backward compatibility. +MIDIIN = pygame.locals.MIDIIN +MIDIOUT = pygame.locals.MIDIOUT + + +_init = False +_pypm = None + + +__all__ = [ + "Input", + "MIDIIN", + "MIDIOUT", + "MidiException", + "Output", + "get_count", + "get_default_input_id", + "get_default_output_id", + "get_device_info", + "init", + "midis2events", + "quit", + "get_init", + "time", + "frequency_to_midi", + "midi_to_frequency", + "midi_to_ansi_note", +] + +__theclasses__ = ["Input", "Output"] + + +def init(): + """initialize the midi module + pygame.midi.init(): return None + + Call the initialisation function before using the midi module. + + It is safe to call this more than once. + """ + global _init, _pypm + if not _init: + import pygame.pypm + _pypm = pygame.pypm + + _pypm.Initialize() + _init = True + atexit.register(quit) + + +def quit(): + """uninitialize the midi module + pygame.midi.quit(): return None + + + Called automatically atexit if you don't call it. + + It is safe to call this function more than once. + """ + global _init, _pypm + if _init: + # TODO: find all Input and Output classes and close them first? + _pypm.Terminate() + _init = False + _pypm = None + + +def get_init(): + """returns True if the midi module is currently initialized + pygame.midi.get_init(): return bool + + Returns True if the pygame.midi module is currently initialized. + + New in pygame 1.9.5. + """ + return _init + + +def _check_init(): + if not _init: + raise RuntimeError("pygame.midi not initialised.") + +def get_count(): + """gets the number of devices. + pygame.midi.get_count(): return num_devices + + + Device ids range from 0 to get_count() -1 + """ + _check_init() + return _pypm.CountDevices() + + + + +def get_default_input_id(): + """gets default input device number + pygame.midi.get_default_input_id(): return default_id + + + Return the default device ID or -1 if there are no devices. + The result can be passed to the Input()/Output() class. + + On the PC, the user can specify a default device by + setting an environment variable. For example, to use device #1. + + set PM_RECOMMENDED_INPUT_DEVICE=1 + + The user should first determine the available device ID by using + the supplied application "testin" or "testout". + + In general, the registry is a better place for this kind of info, + and with USB devices that can come and go, using integers is not + very reliable for device identification. Under Windows, if + PM_RECOMMENDED_OUTPUT_DEVICE (or PM_RECOMMENDED_INPUT_DEVICE) is + *NOT* found in the environment, then the default device is obtained + by looking for a string in the registry under: + HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Input_Device + and HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Output_Device + for a string. The number of the first device with a substring that + matches the string exactly is returned. For example, if the string + in the registry is "USB", and device 1 is named + "In USB MidiSport 1x1", then that will be the default + input because it contains the string "USB". + + In addition to the name, get_device_info() returns "interf", which + is the interface name. (The "interface" is the underlying software + system or API used by PortMidi to access devices. Examples are + MMSystem, DirectX (not implemented), ALSA, OSS (not implemented), etc.) + At present, the only Win32 interface is "MMSystem", the only Linux + interface is "ALSA", and the only Max OS X interface is "CoreMIDI". + To specify both the interface and the device name in the registry, + separate the two with a comma and a space, e.g.: + MMSystem, In USB MidiSport 1x1 + In this case, the string before the comma must be a substring of + the "interf" string, and the string after the space must be a + substring of the "name" name string in order to match the device. + + Note: in the current release, the default is simply the first device + (the input or output device with the lowest PmDeviceID). + """ + _check_init() + return _pypm.GetDefaultInputDeviceID() + + + + +def get_default_output_id(): + """gets default output device number + pygame.midi.get_default_output_id(): return default_id + + + Return the default device ID or -1 if there are no devices. + The result can be passed to the Input()/Output() class. + + On the PC, the user can specify a default device by + setting an environment variable. For example, to use device #1. + + set PM_RECOMMENDED_OUTPUT_DEVICE=1 + + The user should first determine the available device ID by using + the supplied application "testin" or "testout". + + In general, the registry is a better place for this kind of info, + and with USB devices that can come and go, using integers is not + very reliable for device identification. Under Windows, if + PM_RECOMMENDED_OUTPUT_DEVICE (or PM_RECOMMENDED_INPUT_DEVICE) is + *NOT* found in the environment, then the default device is obtained + by looking for a string in the registry under: + HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Input_Device + and HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Output_Device + for a string. The number of the first device with a substring that + matches the string exactly is returned. For example, if the string + in the registry is "USB", and device 1 is named + "In USB MidiSport 1x1", then that will be the default + input because it contains the string "USB". + + In addition to the name, get_device_info() returns "interf", which + is the interface name. (The "interface" is the underlying software + system or API used by PortMidi to access devices. Examples are + MMSystem, DirectX (not implemented), ALSA, OSS (not implemented), etc.) + At present, the only Win32 interface is "MMSystem", the only Linux + interface is "ALSA", and the only Max OS X interface is "CoreMIDI". + To specify both the interface and the device name in the registry, + separate the two with a comma and a space, e.g.: + MMSystem, In USB MidiSport 1x1 + In this case, the string before the comma must be a substring of + the "interf" string, and the string after the space must be a + substring of the "name" name string in order to match the device. + + Note: in the current release, the default is simply the first device + (the input or output device with the lowest PmDeviceID). + """ + _check_init() + return _pypm.GetDefaultOutputDeviceID() + + +def get_device_info(an_id): + """ returns information about a midi device + pygame.midi.get_device_info(an_id): return (interf, name, input, output, opened) + + interf - a text string describing the device interface, eg 'ALSA'. + name - a text string for the name of the device, eg 'Midi Through Port-0' + input - 0, or 1 if the device is an input device. + output - 0, or 1 if the device is an output device. + opened - 0, or 1 if the device is opened. + + If the id is out of range, the function returns None. + """ + _check_init() + return _pypm.GetDeviceInfo(an_id) + + +class Input(object): + """Input is used to get midi input from midi devices. + Input(device_id) + Input(device_id, buffer_size) + + buffer_size - the number of input events to be buffered waiting to + be read using Input.read() + """ + + def __init__(self, device_id, buffer_size=4096): + """ + The buffer_size specifies the number of input events to be buffered + waiting to be read using Input.read(). + """ + _check_init() + + if device_id == -1: + raise MidiException("Device id is -1, not a valid output id. -1 usually means there were no default Output devices.") + + try: + r = get_device_info(device_id) + except TypeError: + raise TypeError("an integer is required") + except OverflowError: + raise OverflowError("long int too large to convert to int") + + # and now some nasty looking error checking, to provide nice error + # messages to the kind, lovely, midi using people of wherever. + if r: + interf, name, input, output, opened = r + if input: + try: + self._input = _pypm.Input(device_id, buffer_size) + except TypeError: + raise TypeError("an integer is required") + self.device_id = device_id + + elif output: + raise MidiException("Device id given is not a valid input id, it is an output id.") + else: + raise MidiException("Device id given is not a valid input id.") + else: + raise MidiException("Device id invalid, out of range.") + + + + + def _check_open(self): + if self._input is None: + raise MidiException("midi not open.") + + + + def close(self): + """ closes a midi stream, flushing any pending buffers. + Input.close(): return None + + PortMidi attempts to close open streams when the application + exits -- this is particularly difficult under Windows. + """ + _check_init() + if not (self._input is None): + self._input.Close() + self._input = None + + + + def read(self, num_events): + """reads num_events midi events from the buffer. + Input.read(num_events): return midi_event_list + + Reads from the Input buffer and gives back midi events. + [[[status,data1,data2,data3],timestamp], + [[status,data1,data2,data3],timestamp],...] + """ + _check_init() + self._check_open() + return self._input.Read(num_events) + + + def poll(self): + """returns true if there's data, or false if not. + Input.poll(): return Bool + + raises a MidiException on error. + """ + _check_init() + self._check_open() + + r = self._input.Poll() + if r == _pypm.TRUE: + return True + elif r == _pypm.FALSE: + return False + else: + err_text = GetErrorText(r) + raise MidiException( (r, err_text) ) + + + + +class Output(object): + """Output is used to send midi to an output device + Output(device_id) + Output(device_id, latency = 0) + Output(device_id, buffer_size = 4096) + Output(device_id, latency, buffer_size) + + The buffer_size specifies the number of output events to be + buffered waiting for output. (In some cases -- see below -- + PortMidi does not buffer output at all and merely passes data + to a lower-level API, in which case buffersize is ignored.) + + latency is the delay in milliseconds applied to timestamps to determine + when the output should actually occur. (If latency is < 0, 0 is + assumed.) + + If latency is zero, timestamps are ignored and all output is delivered + immediately. If latency is greater than zero, output is delayed until + the message timestamp plus the latency. (NOTE: time is measured + relative to the time source indicated by time_proc. Timestamps are + absolute, not relative delays or offsets.) In some cases, PortMidi + can obtain better timing than your application by passing timestamps + along to the device driver or hardware. Latency may also help you + to synchronize midi data to audio data by matching midi latency to + the audio buffer latency. + + """ + + def __init__(self, device_id, latency = 0, buffer_size = 4096): + """Output(device_id) + Output(device_id, latency = 0) + Output(device_id, buffer_size = 4096) + Output(device_id, latency, buffer_size) + + The buffer_size specifies the number of output events to be + buffered waiting for output. (In some cases -- see below -- + PortMidi does not buffer output at all and merely passes data + to a lower-level API, in which case buffersize is ignored.) + + latency is the delay in milliseconds applied to timestamps to determine + when the output should actually occur. (If latency is < 0, 0 is + assumed.) + + If latency is zero, timestamps are ignored and all output is delivered + immediately. If latency is greater than zero, output is delayed until + the message timestamp plus the latency. (NOTE: time is measured + relative to the time source indicated by time_proc. Timestamps are + absolute, not relative delays or offsets.) In some cases, PortMidi + can obtain better timing than your application by passing timestamps + along to the device driver or hardware. Latency may also help you + to synchronize midi data to audio data by matching midi latency to + the audio buffer latency. + """ + + _check_init() + self._aborted = 0 + + if device_id == -1: + raise MidiException("Device id is -1, not a valid output id. -1 usually means there were no default Output devices.") + + try: + r = get_device_info(device_id) + except TypeError: + raise TypeError("an integer is required") + except OverflowError: + raise OverflowError("long int too large to convert to int") + + # and now some nasty looking error checking, to provide nice error + # messages to the kind, lovely, midi using people of whereever. + if r: + interf, name, input, output, opened = r + if output: + try: + self._output = _pypm.Output(device_id, latency) + except TypeError: + raise TypeError("an integer is required") + self.device_id = device_id + + elif input: + raise MidiException("Device id given is not a valid output id, it is an input id.") + else: + raise MidiException("Device id given is not a valid output id.") + else: + raise MidiException("Device id invalid, out of range.") + + def _check_open(self): + if self._output is None: + raise MidiException("midi not open.") + + if self._aborted: + raise MidiException("midi aborted.") + + + def close(self): + """ closes a midi stream, flushing any pending buffers. + Output.close(): return None + + PortMidi attempts to close open streams when the application + exits -- this is particularly difficult under Windows. + """ + _check_init() + if not (self._output is None): + self._output.Close() + self._output = None + + def abort(self): + """terminates outgoing messages immediately + Output.abort(): return None + + The caller should immediately close the output port; + this call may result in transmission of a partial midi message. + There is no abort for Midi input because the user can simply + ignore messages in the buffer and close an input device at + any time. + """ + + _check_init() + if self._output: + self._output.Abort() + self._aborted = 1 + + + + + + def write(self, data): + """writes a list of midi data to the Output + Output.write(data) + + writes series of MIDI information in the form of a list: + write([[[status <,data1><,data2><,data3>],timestamp], + [[status <,data1><,data2><,data3>],timestamp],...]) + fields are optional + example: choose program change 1 at time 20000 and + send note 65 with velocity 100 500 ms later. + write([[[0xc0,0,0],20000],[[0x90,60,100],20500]]) + notes: + 1. timestamps will be ignored if latency = 0. + 2. To get a note to play immediately, send MIDI info with + timestamp read from function Time. + 3. understanding optional data fields: + write([[[0xc0,0,0],20000]]) is equivalent to + write([[[0xc0],20000]]) + + Can send up to 1024 elements in your data list, otherwise an + IndexError exception is raised. + """ + _check_init() + self._check_open() + + self._output.Write(data) + + def write_short(self, status, data1=0, data2=0): + """write_short(status <, data1><, data2>) + Output.write_short(status) + Output.write_short(status, data1 = 0, data2 = 0) + + output MIDI information of 3 bytes or less. + data fields are optional + status byte could be: + 0xc0 = program change + 0x90 = note on + etc. + data bytes are optional and assumed 0 if omitted + example: note 65 on with velocity 100 + write_short(0x90,65,100) + """ + _check_init() + self._check_open() + self._output.WriteShort(status, data1, data2) + + def write_sys_ex(self, when, msg): + """writes a timestamped system-exclusive midi message. + Output.write_sys_ex(when, msg) + + msg - can be a *list* or a *string* + when - a timestamp in miliseconds + example: + (assuming o is an onput MIDI stream) + o.write_sys_ex(0,'\\xF0\\x7D\\x10\\x11\\x12\\x13\\xF7') + is equivalent to + o.write_sys_ex(pygame.midi.time(), + [0xF0,0x7D,0x10,0x11,0x12,0x13,0xF7]) + """ + _check_init() + self._check_open() + self._output.WriteSysEx(when, msg) + + def note_on(self, note, velocity, channel=0): + """turns a midi note on. Note must be off. + Output.note_on(note, velocity, channel=0) + + note is an integer from 0 to 127 + velocity is an integer from 0 to 127 + channel is an integer from 0 to 15 + + Turn a note on in the output stream. The note must already + be off for this to work correctly. + """ + if not (0 <= channel <= 15): + raise ValueError("Channel not between 0 and 15.") + + self.write_short(0x90 + channel, note, velocity) + + def note_off(self, note, velocity=0, channel=0): + """turns a midi note off. Note must be on. + Output.note_off(note, velocity=0, channel=0) + + note is an integer from 0 to 127 + velocity is an integer from 0 to 127 (release velocity) + channel is an integer from 0 to 15 + + Turn a note off in the output stream. The note must already + be on for this to work correctly. + """ + if not (0 <= channel <= 15): + raise ValueError("Channel not between 0 and 15.") + + self.write_short(0x80 + channel, note, velocity) + + + def set_instrument(self, instrument_id, channel=0): + """select an instrument for a channel, with a value between 0 and 127 + Output.set_instrument(instrument_id, channel=0) + + Also called "patch change" or "program change". + """ + if not (0 <= instrument_id <= 127): + raise ValueError("Undefined instrument id: %d" % instrument_id) + + if not (0 <= channel <= 15): + raise ValueError("Channel not between 0 and 15.") + + self.write_short(0xc0 + channel, instrument_id) + + def pitch_bend(self, value=0, channel=0): + """modify the pitch of a channel. + Output.pitch_bend(value=0, channel=0) + + Adjust the pitch of a channel. The value is a signed integer + from -8192 to +8191. For example, 0 means "no change", +4096 is + typically a semitone higher, and -8192 is 1 whole tone lower (though + the musical range corresponding to the pitch bend range can also be + changed in some synthesizers). + + If no value is given, the pitch bend is returned to "no change". + """ + if not (0 <= channel <= 15): + raise ValueError("Channel not between 0 and 15.") + + if not (-8192 <= value <= 8191): + raise ValueError("Pitch bend value must be between " + "-8192 and +8191, not %d." % value) + + # "The 14 bit value of the pitch bend is defined so that a value of + # 0x2000 is the center corresponding to the normal pitch of the note + # (no pitch change)." so value=0 should send 0x2000 + value = value + 0x2000 + LSB = value & 0x7f # keep least 7 bits + MSB = value >> 7 + self.write_short(0xe0 + channel, LSB, MSB) + + + +""" +MIDI commands + + 0x80 Note Off (note_off) + 0x90 Note On (note_on) + 0xA0 Aftertouch + 0xB0 Continuous controller + 0xC0 Patch change (set_instrument?) + 0xD0 Channel Pressure + 0xE0 Pitch bend + 0xF0 (non-musical commands) +""" + + + +def time(): + """returns the current time in ms of the PortMidi timer + pygame.midi.time(): return time + + The time is reset to 0, when the module is inited. + """ + _check_init() + return _pypm.Time() + + + +def midis2events(midis, device_id): + """converts midi events to pygame events + pygame.midi.midis2events(midis, device_id): return [Event, ...] + + Takes a sequence of midi events and returns list of pygame events. + """ + evs = [] + for midi in midis: + + ((status,data1,data2,data3),timestamp) = midi + + e = pygame.event.Event(MIDIIN, + status=status, + data1=data1, + data2=data2, + data3=data3, + timestamp=timestamp, + vice_id = device_id) + evs.append( e ) + + + return evs + + + + + +class MidiException(Exception): + """exception that pygame.midi functions and classes can raise + MidiException(errno) + """ + def __init__(self, value): + self.parameter = value + def __str__(self): + return repr(self.parameter) + + + +def frequency_to_midi(frequency): + """ converts a frequency into a MIDI note. + + Rounds to the closest midi note. + + ::Examples:: + + >>> frequency_to_midi(27.5) + 21 + >>> frequency_to_midi(36.7) + 26 + >>> frequency_to_midi(4186.0) + 108 + """ + return int( + round( + 69 + ( + 12 * math.log(frequency / 440.0) + ) / math.log(2) + ) + ) + +def midi_to_frequency(midi_note): + """ Converts a midi note to a frequency. + + ::Examples:: + + >>> midi_to_frequency(21) + 27.5 + >>> midi_to_frequency(26) + 36.7 + >>> midi_to_frequency(108) + 4186.0 + """ + return round(440.0 * 2 ** ((midi_note - 69) * (1./12.)), 1) + +def midi_to_ansi_note(midi_note): + """ returns the Ansi Note name for a midi number. + + ::Examples:: + + >>> midi_to_ansi_note(21) + 'A0' + >>> midi_to_ansi_note(102) + 'F#7' + >>> midi_to_ansi_note(108) + 'C8' + """ + notes = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#'] + num_notes = 12 + note_name = notes[int(((midi_note - 21) % num_notes))] + note_number = (midi_note - 12) // num_notes + return '%s%s' % (note_name, note_number) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/midi.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/midi.pyi new file mode 100644 index 0000000..84911c1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/midi.pyi @@ -0,0 +1,60 @@ +from typing import Optional, List, Tuple, Union, Sequence + +import pygame.locals +from pygame.event import Event + +MIDIIN: int +MIDIOUT: int + +class MidiException(Exception): + def __init__(self, errno: str) -> None: ... + +def init() -> None: ... +def quit() -> None: ... +def get_init() -> bool: ... +def get_count() -> int: ... +def get_default_input_id() -> int: ... +def get_default_output_id() -> int: ... +def get_device_info(an_id: int) -> Tuple[str, str, int, int, int]: ... +def midis2events( + midi_events: Sequence[Sequence[Union[Sequence[int], int]]], device_id: int +) -> List[Event]: ... +def time() -> int: ... +def frequency_to_midi(frequency: float) -> int: ... +def midi_to_frequency(midi_note: int) -> float: ... +def midi_to_ansi_note(midi_note: int) -> str: ... + +class Input: + device_id: int + def __init__(self, device_id: int, buffer_size: Optional[int] = 4096) -> None: ... + def close(self) -> None: ... + def pool(self) -> bool: ... + def read(self, num_events: int) -> List[List[Union[List[int], int]]]: ... + +class Output: + device_id: int + def __init__( + self, + device_id: int, + latency: Optional[int] = 0, + buffersize: Optional[int] = 4096, + ) -> None: ... + def abort(self) -> None: ... + def close(self) -> None: ... + def note_off( + self, note: int, velocity: Optional[int] = 0, channel: Optional[int] = 0 + ) -> None: ... + def note_on( + self, note: int, velocity: Optional[int] = 0, channel: Optional[int] = 0 + ) -> None: ... + def set_instrument( + self, instrument_id: int, channel: Optional[int] = 0 + ) -> None: ... + def pitch_bend( + self, value: Optional[int] = 0, channel: Optional[int] = 0 + ) -> None: ... + def write(self, data: List[List[Union[List[int], int]]]) -> None: ... + def write_short( + self, status: int, data1: Optional[int] = 0, data2: Optional[int] = 0 + ) -> None: ... + def write_sys_ex(self, msg: Union[List[int], str], when: int) -> None: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/mixer.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/mixer.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..0f3c2e2 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/mixer.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/mixer.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/mixer.pyi new file mode 100644 index 0000000..35d58d7 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/mixer.pyi @@ -0,0 +1,72 @@ +from typing import Optional, Union, Tuple, Any, overload + +from pygame.event import Event +from . import music as music + +def init( + frequency: Optional[int] = 44100, + size: Optional[int] = -16, + channels: Optional[int] = 2, + buffer: Optional[int] = 512, + devicename: Optional[Union[str, None]] = None, + allowedchanges: Optional[int] = 5, +) -> None: ... +def pre_init( + frequency: Optional[int] = 44100, + size: Optional[int] = -16, + channels: Optional[int] = 2, + buffer: Optional[int] = 512, + devicename: Optional[Union[str, None]] = None, +) -> None: ... +def quit() -> None: ... +def get_init() -> Tuple[int, int, int]: ... +def stop() -> None: ... +def pause() -> None: ... +def unpause() -> None: ... +def fadeout(time: int) -> None: ... +def set_num_channels(count: int) -> None: ... +def get_num_channels() -> int: ... +def set_reserved() -> None: ... +def find_channel(force: bool) -> Channel: ... +def get_busy() -> bool: ... +def get_sdl_mixer_version(linked: bool) -> Tuple[int, int, int]: ... + +class Sound: + def __init__(self, name=Any) -> None: ... # ?What should be here? # + def play( + self, + loops: Optional[int] = 0, + maxtime: Optional[int] = 0, + fade_ms: Optional[int] = 0, + ) -> Channel: ... + def stop(self) -> None: ... + def fadeout(self, time: int) -> None: ... + def set_volume(self, value: float) -> None: ... + def get_volume(self) -> float: ... + def get_num_channels(self) -> int: ... + def get_length(self) -> float: ... + def get_raw(self) -> bytes: ... + +class Channel: + def __init__(self, id: int) -> None: ... + def play( + self, + sound: Sound, + loops: Optional[int] = 0, + maxtime: Optional[int] = 0, + fade_ms: Optional[int] = 0, + ) -> None: ... + def stop(self) -> None: ... + def pause(self) -> None: ... + def unpause(self) -> None: ... + def fadeout(self, time: int) -> None: ... + @overload + def set_volume(self, value: float) -> None: ... + @overload + def set_volume(self, left: float, right: float) -> None: ... + def get_volume(self) -> float: ... + def get_busy(self) -> bool: ... + def get_sound(self) -> Sound: ... + def get_queue(self) -> Sound: ... + def set_endevent(self, type: Optional[Union[int, Event]] = None) -> None: ... + def get_endevent(self) -> int: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/mixer_music.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/mixer_music.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..3ab10d8 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/mixer_music.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/mouse.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/mouse.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..9b423f8 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/mouse.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/mouse.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/mouse.pyi new file mode 100644 index 0000000..eb12a61 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/mouse.pyi @@ -0,0 +1,21 @@ +from typing import Tuple, Union, List, overload, Sequence + +def get_pressed() -> Tuple[int, int, int]: ... +def get_pos() -> Tuple[int, int]: ... +def get_rel() -> Tuple[int, int]: ... +@overload +def set_pos(pos: Union[List[float], Tuple[float, float]]) -> None: ... +@overload +def set_pos(x: float, y: float) -> None: ... +def set_visible(value: bool) -> int: ... +def get_visible() -> bool: ... +def get_focused() -> int: ... +def set_cursor( + size: Union[Tuple[int, int], List[int]], + hotspot: Union[Tuple[int, int], List[int]], + xormasks: Sequence[int], + andmasks: Sequence[int], +) -> None: ... # This needs to be checked +def get_cursor() -> Tuple[ + Tuple[int, int], Tuple[int, int], Sequence[int], Sequence[int] +]: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/music.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/music.pyi new file mode 100644 index 0000000..cdd91be --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/music.pyi @@ -0,0 +1,20 @@ +from typing import Optional + +def load(filename: str) -> None: ... +def unload() -> None: ... +def play( + loops: Optional[int] = 0, start: Optional[float] = 0.0, fade_ms: Optional[int] = 0 +): ... +def rewind() -> None: ... +def stop() -> None: ... +def pause() -> None: ... +def unpause() -> None: ... +def fadeout(time: int) -> None: ... +def set_volume(volume: float) -> None: ... +def get_volume() -> float: ... +def get_busy() -> bool: ... +def set_pos(pos: float) -> None: ... +def get_pos() -> int: ... +def queue(filename: str) -> None: ... +def set_endevent(event_type: int) -> None: ... +def get_endevent() -> int: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/newbuffer.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/newbuffer.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..07d702e Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/newbuffer.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/pixelarray.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/pixelarray.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..cc75ae6 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/pixelarray.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/pixelarray.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/pixelarray.pyi new file mode 100644 index 0000000..a7a3a4d --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/pixelarray.pyi @@ -0,0 +1,38 @@ +from typing import Tuple, Union, List, Optional, TypeVar, Sequence + +from pygame.color import Color +from pygame.surface import Surface + +_ColorValue = Union[ + Color, Tuple[int, int, int], List[int], int, Tuple[int, int, int, int] +] + +class PixelArray: + surface: Surface + itemsize: int + ndim: int + shape: Tuple[int, ...] + strides: Tuple[int, ...] + def __init__(self, surface: Surface) -> None: ... + def make_surface(self) -> Surface: ... + def replace( + self, + color: _ColorValue, + repcolor: _ColorValue, + distance: Optional[float] = 0, + weights: Optional[Sequence[float]] = (0.299, 0.587, 0.114), + ) -> None: ... + def extract( + self, + color: _ColorValue, + distance: Optional[float] = 0, + weights: Optional[Sequence[float]] = (0.299, 0.587, 0.114), + ) -> PixelArray: ... + def compare( + self, + array: PixelArray, + distance: Optional[float] = 0, + weights: Optional[Sequence[float]] = (0.299, 0.587, 0.114), + ) -> PixelArray: ... + def transpose(self) -> PixelArray: ... + def close(self) -> PixelArray: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/pixelcopy.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/pixelcopy.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..fde24f4 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/pixelcopy.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/pixelcopy.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/pixelcopy.pyi new file mode 100644 index 0000000..467642b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/pixelcopy.pyi @@ -0,0 +1,24 @@ +from typing import Optional +import sys + +if sys.version_info >= (3, 8): + from typing import Literal +else: + from typing_extensions import Literal +import numpy +from pygame.surface import Surface + +_kind = Literal["P", "p", "R", "r", "G", "g", "B", "b", "A", "a", "C", "c"] + +def surface_to_array( + array: numpy.ndarray, + surface: Surface, + kind: Optional[_kind] = "P", + opaque: Optional[int] = 255, + clear: Optional[int] = 0, +) -> None: ... +def array_to_surface(surface: Surface, array: numpy.ndarray) -> None: ... +def map_to_array( + array1: numpy.ndarray, array2: numpy.ndarray, surface: Surface +) -> None: ... +def make_surface(array: numpy.ndarray) -> Surface: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/pkgdata.py b/Display/.venv/lib/python3.7/site-packages/pygame/pkgdata.py new file mode 100644 index 0000000..25ad64a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/pkgdata.py @@ -0,0 +1,67 @@ +""" +pkgdata is a simple, extensible way for a package to acquire data file +resources. + +The getResource function is equivalent to the standard idioms, such as +the following minimal implementation: + + import sys, os + + def getResource(identifier, pkgname=__name__): + pkgpath = os.path.dirname(sys.modules[pkgname].__file__) + path = os.path.join(pkgpath, identifier) + return file(os.path.normpath(path), mode='rb') + +When a __loader__ is present on the module given by __name__, it will defer +getResource to its get_data implementation and return it as a file-like +object (such as StringIO). +""" + +__all__ = ['getResource'] +import sys +import os +from pygame.compat import get_BytesIO +BytesIO = get_BytesIO() + +try: + from pkg_resources import resource_stream, resource_exists +except ImportError: + def resource_exists(package_or_requirement, resource_name): + return False + def resource_stream(package_of_requirement, resource_name): + raise NotImplementedError + +def getResource(identifier, pkgname=__name__): + """ + Acquire a readable object for a given package name and identifier. + An IOError will be raised if the resource can not be found. + + For example: + mydata = getResource('mypkgdata.jpg').read() + + Note that the package name must be fully qualified, if given, such + that it would be found in sys.modules. + + In some cases, getResource will return a real file object. In that + case, it may be useful to use its name attribute to get the path + rather than use it as a file-like object. For example, you may + be handing data off to a C API. + """ + if resource_exists(pkgname, identifier): + return resource_stream(pkgname, identifier) + + mod = sys.modules[pkgname] + fn = getattr(mod, '__file__', None) + if fn is None: + raise IOError("%s has no __file__!" % repr(mod)) + path = os.path.join(os.path.dirname(fn), identifier) + if sys.version_info < (3, 3): + loader = getattr(mod, '__loader__', None) + if loader is not None: + try: + data = loader.get_data(path) + except IOError: + pass + else: + return BytesIO(data) + return open(os.path.normpath(path), 'rb') diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/pygame.ico b/Display/.venv/lib/python3.7/site-packages/pygame/pygame.ico new file mode 100644 index 0000000..06f699e Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/pygame.ico differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/pygame_icon.bmp b/Display/.venv/lib/python3.7/site-packages/pygame/pygame_icon.bmp new file mode 100644 index 0000000..74aea77 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/pygame_icon.bmp differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/pygame_icon.icns b/Display/.venv/lib/python3.7/site-packages/pygame/pygame_icon.icns new file mode 100644 index 0000000..2610a8d Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/pygame_icon.icns differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/pygame_icon.svg b/Display/.venv/lib/python3.7/site-packages/pygame/pygame_icon.svg new file mode 100644 index 0000000..bbee79d --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/pygame_icon.svg @@ -0,0 +1,259 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/pygame_icon.tiff b/Display/.venv/lib/python3.7/site-packages/pygame/pygame_icon.tiff new file mode 100644 index 0000000..e779143 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/pygame_icon.tiff differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/pypm.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/pypm.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..94a9f96 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/pypm.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/rect.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/rect.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..d9870c3 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/rect.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/rect.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/rect.pyi new file mode 100644 index 0000000..886dc08 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/rect.pyi @@ -0,0 +1,191 @@ +from typing import Dict, List, Sequence, Tuple, TypeVar, Union, overload, Iterable +from pygame.math import Vector2 + +_K = TypeVar("_K") +_V = TypeVar("_V") + +_RectStyle = Union[ + Tuple[float, float, float, float], + Tuple[Tuple[float, float], Tuple[float, float]], + List[float], + List[Vector2], + Tuple[Vector2, Vector2], + Iterable[Vector2], +] +_Coordinate = Union[Tuple[float, float], List[float], Vector2] + +class Rect(object): + x: int + y: int + top: int + left: int + bottom: int + right: int + topleft: Tuple[int, int] + bottomleft: Tuple[int, int] + topright: Tuple[int, int] + bottomright: Tuple[int, int] + midtop: Tuple[int, int] + midleft: Tuple[int, int] + midbottom: Tuple[int, int] + midright: Tuple[int, int] + center: Tuple[int, int] + centerx: int + centery: int + size: Tuple[int, int] + width: int + height: int + w: int + h: int + @overload + def __init__( + self, left: float, top: float, width: float, height: float + ) -> None: ... + @overload + def __init__( + self, + left_top: Union[List[float], Tuple[float, float], Vector2], + width_height: Union[List[float], Tuple[float, float], Vector2], + ) -> None: ... + @overload + def __init__( + self, + left_top_width_height: Union[Tuple[float, float, float, float], List[float]], + ) -> None: ... + def copy(self) -> Rect: ... + def move(self, x: float, y: float) -> Rect: ... + def move_ip(self, x: float, y: float) -> None: ... + def inflate(self, x: float, y: float) -> Rect: ... + def inflate_ip(self, x: float, y: float) -> None: ... + @overload + def clamp(self, rect: Union[_RectStyle, Rect]) -> Rect: ... + @overload + def clamp( + self, + left_top: Union[List[float], Tuple[float, float], Vector2], + width_height: Union[List[float], Tuple[float, float], Vector2], + ) -> Rect: ... + @overload + def clamp(self, left: float, top: float, width: float, height: float) -> Rect: ... + @overload + def clamp_ip(self, rect: Union[_RectStyle, Rect]) -> None: ... + @overload + def clamp_ip( + self, + left_top: Union[List[float], Tuple[float, float], Vector2], + width_height: Union[List[float], Tuple[float, float], Vector2], + ) -> None: ... + @overload + def clamp_ip( + self, left: float, top: float, width: float, height: float + ) -> None: ... + @overload + def clip(self, rect: Union[_RectStyle, Rect]) -> Rect: ... + @overload + def clip( + self, + left_top: Union[List[float], Tuple[float, float], Vector2], + width_height: Union[List[float], Tuple[float, float], Vector2], + ) -> Rect: ... + @overload + def clip(self, left: float, top: float, width: float, height: float) -> Rect: ... + @overload + def clipline( + self, x1: float, x2: float, x3: float, x4: float + ) -> Union[Tuple[Tuple[int, int], Tuple[int, int]], Tuple[()]]: ... + @overload + def clipline( + self, first_coordinate: _Coordinate, second_coordinate: _Coordinate + ) -> Union[Tuple[Tuple[int, int], Tuple[int, int]], Tuple[()]]: ... + @overload + def clipline( + self, values: Union[Tuple[float, float, float, float], List[float]] + ) -> Union[Tuple[Tuple[int, int], Tuple[int, int]], Tuple[()]]: ... + @overload + def clipline( + self, coordinates: Union[Tuple[_Coordinate, _Coordinate], List[_Coordinate]] + ) -> Union[Tuple[Tuple[int, int], Tuple[int, int]], Tuple[()]]: ... + @overload + def union(self, rect: Union[_RectStyle, Rect]) -> Rect: ... + @overload + def union( + self, + left_top: Union[List[float], Tuple[float, float], Vector2], + width_height: Union[List[float], Tuple[float, float], Vector2], + ) -> Rect: ... + @overload + def union(self, left: float, top: float, width: float, height: float) -> Rect: ... + @overload + def union_ip(self, rect: Union[_RectStyle, Rect]) -> None: ... + @overload + def union_ip( + self, + left_top: Union[List[float], Tuple[float, float], Vector2], + width_height: Union[List[float], Tuple[float, float], Vector2], + ) -> None: ... + @overload + def union_ip( + self, left: float, top: float, width: float, height: float + ) -> None: ... + def unionall(self, rect: Sequence[Union[_RectStyle, Rect]]) -> Rect: ... + def unionall_ip(self, rect_sequence: Sequence[Union[_RectStyle, Rect]]) -> None: ... + @overload + def fit(self, rect: Union[_RectStyle, Rect]) -> Rect: ... + @overload + def fit( + self, + left_top: Union[List[float], Tuple[float, float], Vector2], + width_height: Union[List[float], Tuple[float, float], Vector2], + ) -> Rect: ... + @overload + def fit(self, left: float, top: float, width: float, height: float) -> Rect: ... + def normalize(self) -> None: ... + @overload + def contains(self, rect: Union[_RectStyle, Rect]) -> int: ... + @overload + def contains( + self, + left_top: Union[List[float], Tuple[float, float], Vector2], + width_height: Union[List[float], Tuple[float, float], Vector2], + ) -> int: ... + @overload + def contains(self, left: float, top: float, width: float, height: float) -> int: ... + @overload + def collidepoint(self, x: float, y: float) -> int: ... + @overload + def collidepoint(self, x_y: Union[List[float], Tuple[float, float]]) -> int: ... + @overload + def colliderect(self, rect: Union[_RectStyle, Rect]) -> int: ... + @overload + def colliderect( + self, + left_top: Union[List[float], Tuple[float, float], Vector2], + width_height: Union[List[float], Tuple[float, float], Vector2], + ) -> int: ... + @overload + def colliderect( + self, left: float, top: float, width: float, height: float + ) -> int: ... + def collidelist(self, rect_list: Sequence[Union[Rect, _RectStyle]]) -> int: ... + def collidelistall( + self, rect_list: Sequence[Union[Rect, _RectStyle]] + ) -> List[int]: ... + # Also undocumented: the dict collision methods take a 'values' argument + # that defaults to False. If it is False, the keys in rect_dict must be + # Rect-like; otherwise, the values must be Rects. + @overload + def collidedict( + self, rect_dict: Dict[_RectStyle, _V], values: bool = ... + ) -> Tuple[_RectStyle, _V]: ... + @overload + def collidedict( + self, rect_dict: Dict[_K, "Rect"], values: bool + ) -> Tuple[_K, "Rect"]: ... + @overload + def collidedictall( + self, rect_dict: Dict[_RectStyle, _V], values: bool = ... + ) -> List[Tuple[_RectStyle, _V]]: ... + @overload + def collidedictall( + self, rect_dict: Dict[_K, "Rect"], values: bool + ) -> List[Tuple[_K, "Rect"]]: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/rwobject.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/rwobject.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..4e08293 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/rwobject.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/scrap.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/scrap.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..cf099ad Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/scrap.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/scrap.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/scrap.pyi new file mode 100644 index 0000000..7905ce6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/scrap.pyi @@ -0,0 +1,10 @@ +from typing import AnyStr, List + +def init() -> None: ... +def get_init() -> bool: ... +def get(data_type: str) -> AnyStr: ... +def get_types() -> List[str]: ... +def put(data_type: str, data: AnyStr) -> None: ... +def contains(data_type: str) -> bool: ... +def lost() -> bool: ... +def set_mode(mode: int) -> None: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/sndarray.py b/Display/.venv/lib/python3.7/site-packages/pygame/sndarray.py new file mode 100644 index 0000000..da5eb89 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/sndarray.py @@ -0,0 +1,120 @@ +## pygame - Python Game Library +## Copyright (C) 2008 Marcus von Appen +## +## This library is free software; you can redistribute it and/or +## modify it under the terms of the GNU Library General Public +## License as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## Library General Public License for more details. +## +## You should have received a copy of the GNU Library General Public +## License along with this library; if not, write to the Free +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## +## Marcus von Appen +## mva@sysfault.org + +"""pygame module for accessing sound sample data + +Functions to convert between numpy arrays and Sound +objects. This module will only be available when pygame can use the +external numpy package. + +Sound data is made of thousands of samples per second, and each sample +is the amplitude of the wave at a particular moment in time. For +example, in 22-kHz format, element number 5 of the array is the +amplitude of the wave after 5/22000 seconds. + +Each sample is an 8-bit or 16-bit integer, depending on the data format. +A stereo sound file has two values per sample, while a mono sound file +only has one. + +Supported array systems are + + numpy + +The array type to use can be changed at runtime using the use_arraytype() +method, which requires one of the above types as string. + +Sounds with 16-bit data will be treated as unsigned integers, +if the sound sample type requests this. +""" + +# import pygame._numpysndarray as numpysnd +numpysnd = None + +def array (sound): + """pygame.sndarray.array(Sound): return array + + Copy Sound samples into an array. + + Creates a new array for the sound data and copies the samples. The + array will always be in the format returned from + pygame.mixer.get_init(). + """ + global numpysnd + try: + return numpysnd.array (sound) + except AttributeError: + import pygame._numpysndarray as numpysnd + return numpysnd.array (sound) + + +def samples (sound): + """pygame.sndarray.samples(Sound): return array + + Reference Sound samples into an array. + + Creates a new array that directly references the samples in a Sound + object. Modifying the array will change the Sound. The array will + always be in the format returned from pygame.mixer.get_init(). + """ + global numpysnd + try: + return numpysnd.samples (sound) + except AttributeError: + import pygame._numpysndarray as numpysnd + return numpysnd.samples (sound) + +def make_sound (array): + """pygame.sndarray.make_sound(array): return Sound + + Convert an array into a Sound object. + + Create a new playable Sound object from an array. The mixer module + must be initialized and the array format must be similar to the mixer + audio format. + """ + global numpysnd + try: + return numpysnd.make_sound (array) + except AttributeError: + import pygame._numpysndarray as numpysnd + return numpysnd.make_sound (array) + +def use_arraytype (arraytype): + """pygame.sndarray.use_arraytype (arraytype): return None + + DEPRECATED - only numpy arrays are now supported. + """ + arraytype = arraytype.lower () + if arraytype != 'numpy': + raise ValueError("invalid array type") + +def get_arraytype (): + """pygame.sndarray.get_arraytype (): return str + + DEPRECATED - only numpy arrays are now supported. + """ + return 'numpy' + +def get_arraytypes (): + """pygame.sndarray.get_arraytypes (): return tuple + + DEPRECATED - only numpy arrays are now supported. + """ + return ('numpy',) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/sndarray.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/sndarray.pyi new file mode 100644 index 0000000..789ab73 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/sndarray.pyi @@ -0,0 +1,10 @@ +from typing import Tuple +from pygame.mixer import Sound +import numpy + +def array(sound: Sound) -> numpy.ndarray: ... +def samples(sound: Sound) -> numpy.ndarray: ... +def make_sound(array: numpy.ndarray) -> Sound: ... +def use_arraytype(arraytype: str) -> Sound: ... +def get_arraytype() -> str: ... +def get_arraytypes() -> Tuple[str]: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/sprite.py b/Display/.venv/lib/python3.7/site-packages/pygame/sprite.py new file mode 100644 index 0000000..8e17c00 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/sprite.py @@ -0,0 +1,1607 @@ +## pygame - Python Game Library +## Copyright (C) 2000-2003, 2007 Pete Shinners +## (C) 2004 Joe Wreschnig +## This library is free software; you can redistribute it and/or +## modify it under the terms of the GNU Library General Public +## License as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## Library General Public License for more details. +## +## You should have received a copy of the GNU Library General Public +## License along with this library; if not, write to the Free +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## +## Pete Shinners +## pete@shinners.org + +"""pygame module with basic game object classes + +This module contains several simple classes to be used within games. There +are the main Sprite class and several Group classes that contain Sprites. +The use of these classes is entirely optional when using Pygame. The classes +are fairly lightweight and only provide a starting place for the code +that is common to most games. + +The Sprite class is intended to be used as a base class for the different +types of objects in the game. There is also a base Group class that simply +stores sprites. A game could create new types of Group classes that operate +on specially customized Sprite instances they contain. + +The basic Sprite class can draw the Sprites it contains to a Surface. The +Group.draw() method requires that each Sprite have a Surface.image attribute +and a Surface.rect. The Group.clear() method requires these same attributes +and can be used to erase all the Sprites with background. There are also +more advanced Groups: pygame.sprite.RenderUpdates() and +pygame.sprite.OrderedUpdates(). + +Lastly, this module contains several collision functions. These help find +sprites inside multiple groups that have intersecting bounding rectangles. +To find the collisions, the Sprites are required to have a Surface.rect +attribute assigned. + +The groups are designed for high efficiency in removing and adding Sprites +to them. They also allow cheap testing to see if a Sprite already exists in +a Group. A given Sprite can exist in any number of groups. A game could use +some groups to control object rendering, and a completely separate set of +groups to control interaction or player movement. Instead of adding type +attributes or bools to a derived Sprite class, consider keeping the +Sprites inside organized Groups. This will allow for easier lookup later +in the game. + +Sprites and Groups manage their relationships with the add() and remove() +methods. These methods can accept a single or multiple group arguments for +membership. The default initializers for these classes also take a +single group or list of groups as arguments for initial membership. It is safe +to repeatedly add and remove the same Sprite from a Group. + +While it is possible to design sprite and group classes that don't derive +from the Sprite and AbstractGroup classes below, it is strongly recommended +that you extend those when you create a new Sprite or Group class. + +Sprites are not thread safe, so lock them yourself if using threads. + +""" + +##todo +## a group that holds only the 'n' most recent elements. +## sort of like the GroupSingle class, but holding more +## than one sprite +## +## drawing groups that can 'automatically' store the area +## underneath so they can "clear" without needing a background +## function. obviously a little slower than normal, but nice +## to use in many situations. (also remember it must "clear" +## in the reverse order that it draws :]) +## +## the drawing groups should also be able to take a background +## function, instead of just a background surface. the function +## would take a surface and a rectangle on that surface to erase. +## +## perhaps more types of collision functions? the current two +## should handle just about every need, but perhaps more optimized +## specific ones that aren't quite so general but fit into common +## specialized cases. + +import pygame +from pygame import Rect +from pygame.time import get_ticks +from operator import truth + +# Python 3 does not have the callable function, but an equivalent can be made +# with the hasattr function. +if 'callable' not in dir(__builtins__): + callable = lambda obj: hasattr(obj, '__call__') + +# Don't depend on pygame.mask if it's not there... +try: + from pygame.mask import from_surface +except ImportError: + pass + + +class Sprite(object): + """simple base class for visible game objects + + pygame.sprite.Sprite(*groups): return Sprite + + The base class for visible game objects. Derived classes will want to + override the Sprite.update() method and assign Sprite.image and Sprite.rect + attributes. The initializer can accept any number of Group instances that + the Sprite will become a member of. + + When subclassing the Sprite class, be sure to call the base initializer + before adding the Sprite to Groups. + + """ + + def __init__(self, *groups): + self.__g = {} # The groups the sprite is in + if groups: + self.add(*groups) + + def add(self, *groups): + """add the sprite to groups + + Sprite.add(*groups): return None + + Any number of Group instances can be passed as arguments. The + Sprite will be added to the Groups it is not already a member of. + + """ + has = self.__g.__contains__ + for group in groups: + if hasattr(group, '_spritegroup'): + if not has(group): + group.add_internal(self) + self.add_internal(group) + else: + self.add(*group) + + def remove(self, *groups): + """remove the sprite from groups + + Sprite.remove(*groups): return None + + Any number of Group instances can be passed as arguments. The Sprite + will be removed from the Groups it is currently a member of. + + """ + has = self.__g.__contains__ + for group in groups: + if hasattr(group, '_spritegroup'): + if has(group): + group.remove_internal(self) + self.remove_internal(group) + else: + self.remove(*group) + + def add_internal(self, group): + self.__g[group] = 0 + + def remove_internal(self, group): + del self.__g[group] + + def update(self, *args, **kwargs): + """method to control sprite behavior + + Sprite.update(*args, **kwargs): + + The default implementation of this method does nothing; it's just a + convenient "hook" that you can override. This method is called by + Group.update() with whatever arguments you give it. + + There is no need to use this method if not using the convenience + method by the same name in the Group class. + + """ + pass + + def kill(self): + """remove the Sprite from all Groups + + Sprite.kill(): return None + + The Sprite is removed from all the Groups that contain it. This won't + change anything about the state of the Sprite. It is possible to + continue to use the Sprite after this method has been called, including + adding it to Groups. + + """ + for c in self.__g: + c.remove_internal(self) + self.__g.clear() + + def groups(self): + """list of Groups that contain this Sprite + + Sprite.groups(): return group_list + + Returns a list of all the Groups that contain this Sprite. + + """ + return list(self.__g) + + def alive(self): + """does the sprite belong to any groups + + Sprite.alive(): return bool + + Returns True when the Sprite belongs to one or more Groups. + """ + return truth(self.__g) + + def __repr__(self): + return "<%s sprite(in %d groups)>" % (self.__class__.__name__, len(self.__g)) + + +class DirtySprite(Sprite): + """a more featureful subclass of Sprite with more attributes + + pygame.sprite.DirtySprite(*groups): return DirtySprite + + Extra DirtySprite attributes with their default values: + + dirty = 1 + If set to 1, it is repainted and then set to 0 again. + If set to 2, it is always dirty (repainted each frame; + flag is not reset). + If set to 0, it is not dirty and therefore not repainted again. + + blendmode = 0 + It's the special_flags argument of Surface.blit; see the blendmodes in + the Surface.blit documentation + + source_rect = None + This is the source rect to use. Remember that it is relative to the top + left corner (0, 0) of self.image. + + visible = 1 + Normally this is 1. If set to 0, it will not be repainted. (If you + change visible to 1, you must set dirty to 1 for it to be erased from + the screen.) + + _layer = 0 + 0 is the default value but this is able to be set differently + when subclassing. + + """ + + def __init__(self, *groups): + + self.dirty = 1 + self.blendmode = 0 # pygame 1.8, referred to as special_flags in + # the documentation of Surface.blit + self._visible = 1 + self._layer = getattr(self, '_layer', 0) # Default 0 unless + # initialized differently. + self.source_rect = None + Sprite.__init__(self, *groups) + + def _set_visible(self, val): + """set the visible value (0 or 1) and makes the sprite dirty""" + self._visible = val + if self.dirty < 2: + self.dirty = 1 + + def _get_visible(self): + """return the visible value of that sprite""" + return self._visible + + visible = property(lambda self: self._get_visible(), + lambda self, value: self._set_visible(value), + doc="you can make this sprite disappear without " + "removing it from the group,\n" + "assign 0 for invisible and 1 for visible") + + def __repr__(self): + return "<%s DirtySprite(in %d groups)>" % \ + (self.__class__.__name__, len(self.groups())) + + +class AbstractGroup(object): + """base class for containers of sprites + + AbstractGroup does everything needed to behave as a normal group. You can + easily subclass a new group class from this or the other groups below if + you want to add more features. + + Any AbstractGroup-derived sprite groups act like sequences and support + iteration, len, and so on. + + """ + + # dummy val to identify sprite groups, and avoid infinite recursion + _spritegroup = True + + def __init__(self): + self.spritedict = {} + self.lostsprites = [] + + def sprites(self): + """get a list of sprites in the group + + Group.sprite(): return list + + Returns an object that can be looped over with a 'for' loop. (For now, + it is always a list, but this could change in a future version of + pygame.) Alternatively, you can get the same information by iterating + directly over the sprite group, e.g. 'for sprite in group'. + + """ + return list(self.spritedict) + + def add_internal(self, sprite): + self.spritedict[sprite] = 0 + + def remove_internal(self, sprite): + r = self.spritedict[sprite] + if r: + self.lostsprites.append(r) + del self.spritedict[sprite] + + def has_internal(self, sprite): + return sprite in self.spritedict + + def copy(self): + """copy a group with all the same sprites + + Group.copy(): return Group + + Returns a copy of the group that is an instance of the same class + and has the same sprites in it. + + """ + return self.__class__(self.sprites()) + + def __iter__(self): + return iter(self.sprites()) + + def __contains__(self, sprite): + return self.has(sprite) + + def add(self, *sprites): + """add sprite(s) to group + + Group.add(sprite, list, group, ...): return None + + Adds a sprite or sequence of sprites to a group. + + """ + for sprite in sprites: + # It's possible that some sprite is also an iterator. + # If this is the case, we should add the sprite itself, + # and not the iterator object. + if isinstance(sprite, Sprite): + if not self.has_internal(sprite): + self.add_internal(sprite) + sprite.add_internal(self) + else: + try: + # See if sprite is an iterator, like a list or sprite + # group. + self.add(*sprite) + except (TypeError, AttributeError): + # Not iterable. This is probably a sprite that is not an + # instance of the Sprite class or is not an instance of a + # subclass of the Sprite class. Alternately, it could be an + # old-style sprite group. + if hasattr(sprite, '_spritegroup'): + for spr in sprite.sprites(): + if not self.has_internal(spr): + self.add_internal(spr) + spr.add_internal(self) + elif not self.has_internal(sprite): + self.add_internal(sprite) + sprite.add_internal(self) + + def remove(self, *sprites): + """remove sprite(s) from group + + Group.remove(sprite, list, or group, ...): return None + + Removes a sprite or sequence of sprites from a group. + + """ + # This function behaves essentially the same as Group.add. It first + # tries to handle each argument as an instance of the Sprite class. If + # that failes, then it tries to handle the argument as an iterable + # object. If that failes, then it tries to handle the argument as an + # old-style sprite group. Lastly, if that fails, it assumes that the + # normal Sprite methods should be used. + for sprite in sprites: + if isinstance(sprite, Sprite): + if self.has_internal(sprite): + self.remove_internal(sprite) + sprite.remove_internal(self) + else: + try: + self.remove(*sprite) + except (TypeError, AttributeError): + if hasattr(sprite, '_spritegroup'): + for spr in sprite.sprites(): + if self.has_internal(spr): + self.remove_internal(spr) + spr.remove_internal(self) + elif self.has_internal(sprite): + self.remove_internal(sprite) + sprite.remove_internal(self) + + def has(self, *sprites): + """ask if group has a sprite or sprites + + Group.has(sprite or group, ...): return bool + + Returns True if the given sprite or sprites are contained in the + group. Alternatively, you can get the same information using the + 'in' operator, e.g. 'sprite in group', 'subgroup in group'. + + """ + return_value = False + + for sprite in sprites: + if isinstance(sprite, Sprite): + # Check for Sprite instance's membership in this group + if self.has_internal(sprite): + return_value = True + else: + return False + else: + try: + if self.has(*sprite): + return_value = True + else: + return False + except (TypeError, AttributeError): + if hasattr(sprite, '_spritegroup'): + for spr in sprite.sprites(): + if self.has_internal(spr): + return_value = True + else: + return False + else: + if self.has_internal(sprite): + return_value = True + else: + return False + + return return_value + + def update(self, *args, **kwargs): + """call the update method of every member sprite + + Group.update(*args, **kwargs): return None + + Calls the update method of every member sprite. All arguments that + were passed to this method are passed to the Sprite update function. + + """ + for s in self.sprites(): + s.update(*args, **kwargs) + + def draw(self, surface): + """draw all sprites onto the surface + + Group.draw(surface): return None + + Draws all of the member sprites onto the given surface. + + """ + sprites = self.sprites() + surface_blit = surface.blit + for spr in sprites: + self.spritedict[spr] = surface_blit(spr.image, spr.rect) + self.lostsprites = [] + + def clear(self, surface, bgd): + """erase the previous position of all sprites + + Group.clear(surface, bgd): return None + + Clears the area under every drawn sprite in the group. The bgd + argument should be Surface which is the same dimensions as the + screen surface. The bgd could also be a function which accepts + the given surface and the area to be cleared as arguments. + + """ + if callable(bgd): + for r in self.lostsprites: + bgd(surface, r) + for r in self.spritedict.values(): + if r: + bgd(surface, r) + else: + surface_blit = surface.blit + for r in self.lostsprites: + surface_blit(bgd, r, r) + for r in self.spritedict.values(): + if r: + surface_blit(bgd, r, r) + + def empty(self): + """remove all sprites + + Group.empty(): return None + + Removes all the sprites from the group. + + """ + for s in self.sprites(): + self.remove_internal(s) + s.remove_internal(self) + + def __nonzero__(self): + return truth(self.sprites()) + + def __len__(self): + """return number of sprites in group + + Group.len(group): return int + + Returns the number of sprites contained in the group. + + """ + return len(self.sprites()) + + def __repr__(self): + return "<%s(%d sprites)>" % (self.__class__.__name__, len(self)) + +class Group(AbstractGroup): + """container class for many Sprites + + pygame.sprite.Group(*sprites): return Group + + A simple container for Sprite objects. This class can be subclassed to + create containers with more specific behaviors. The constructor takes any + number of Sprite arguments to add to the Group. The group supports the + following standard Python operations: + + in test if a Sprite is contained + len the number of Sprites contained + bool test if any Sprites are contained + iter iterate through all the Sprites + + The Sprites in the Group are not ordered, so the Sprites are drawn and + iterated over in no particular order. + + """ + def __init__(self, *sprites): + AbstractGroup.__init__(self) + self.add(*sprites) + +RenderPlain = Group +RenderClear = Group + +class RenderUpdates(Group): + """Group class that tracks dirty updates + + pygame.sprite.RenderUpdates(*sprites): return RenderUpdates + + This class is derived from pygame.sprite.Group(). It has an enhanced draw + method that tracks the changed areas of the screen. + + """ + def draw(self, surface): + spritedict = self.spritedict + surface_blit = surface.blit + dirty = self.lostsprites + self.lostsprites = [] + dirty_append = dirty.append + for s in self.sprites(): + r = spritedict[s] + newrect = surface_blit(s.image, s.rect) + if r: + if newrect.colliderect(r): + dirty_append(newrect.union(r)) + else: + dirty_append(newrect) + dirty_append(r) + else: + dirty_append(newrect) + spritedict[s] = newrect + return dirty + +class OrderedUpdates(RenderUpdates): + """RenderUpdates class that draws Sprites in order of addition + + pygame.sprite.OrderedUpdates(*spites): return OrderedUpdates + + This class derives from pygame.sprite.RenderUpdates(). It maintains + the order in which the Sprites were added to the Group for rendering. + This makes adding and removing Sprites from the Group a little + slower than regular Groups. + + """ + def __init__(self, *sprites): + self._spritelist = [] + RenderUpdates.__init__(self, *sprites) + + def sprites(self): + return list(self._spritelist) + + def add_internal(self, sprite): + RenderUpdates.add_internal(self, sprite) + self._spritelist.append(sprite) + + def remove_internal(self, sprite): + RenderUpdates.remove_internal(self, sprite) + self._spritelist.remove(sprite) + + +class LayeredUpdates(AbstractGroup): + """LayeredUpdates Group handles layers, which are drawn like OrderedUpdates + + pygame.sprite.LayeredUpdates(*spites, **kwargs): return LayeredUpdates + + This group is fully compatible with pygame.sprite.Sprite. + New in pygame 1.8.0 + + """ + + _init_rect = Rect(0, 0, 0, 0) + + def __init__(self, *sprites, **kwargs): + """initialize an instance of LayeredUpdates with the given attributes + + You can set the default layer through kwargs using 'default_layer' + and an integer for the layer. The default layer is 0. + + If the sprite you add has an attribute _layer, then that layer will be + used. If **kwarg contains 'layer', then the passed sprites will be + added to that layer (overriding the sprite._layer attribute). If + neither the sprite nor **kwarg has a 'layer', then the default layer is + used to add the sprites. + + """ + self._spritelayers = {} + self._spritelist = [] + AbstractGroup.__init__(self) + self._default_layer = kwargs.get('default_layer', 0) + + self.add(*sprites, **kwargs) + + def add_internal(self, sprite, layer=None): + """Do not use this method directly. + + It is used by the group to add a sprite internally. + + """ + self.spritedict[sprite] = self._init_rect + + if layer is None: + try: + layer = sprite._layer + except AttributeError: + layer = sprite._layer = self._default_layer + elif hasattr(sprite, '_layer'): + sprite._layer = layer + + sprites = self._spritelist # speedup + sprites_layers = self._spritelayers + sprites_layers[sprite] = layer + + # add the sprite at the right position + # bisect algorithmus + leng = len(sprites) + low = mid = 0 + high = leng - 1 + while low <= high: + mid = low + (high - low) // 2 + if sprites_layers[sprites[mid]] <= layer: + low = mid + 1 + else: + high = mid - 1 + # linear search to find final position + while mid < leng and sprites_layers[sprites[mid]] <= layer: + mid += 1 + sprites.insert(mid, sprite) + + def add(self, *sprites, **kwargs): + """add a sprite or sequence of sprites to a group + + LayeredUpdates.add(*sprites, **kwargs): return None + + If the sprite you add has an attribute _layer, then that layer will be + used. If **kwarg contains 'layer', then the passed sprites will be + added to that layer (overriding the sprite._layer attribute). If + neither the sprite nor **kwarg has a 'layer', then the default layer is + used to add the sprites. + + """ + + if not sprites: + return + if 'layer' in kwargs: + layer = kwargs['layer'] + else: + layer = None + for sprite in sprites: + # It's possible that some sprite is also an iterator. + # If this is the case, we should add the sprite itself, + # and not the iterator object. + if isinstance(sprite, Sprite): + if not self.has_internal(sprite): + self.add_internal(sprite, layer) + sprite.add_internal(self) + else: + try: + # See if sprite is an iterator, like a list or sprite + # group. + self.add(*sprite, **kwargs) + except (TypeError, AttributeError): + # Not iterable. This is probably a sprite that is not an + # instance of the Sprite class or is not an instance of a + # subclass of the Sprite class. Alternately, it could be an + # old-style sprite group. + if hasattr(sprite, '_spritegroup'): + for spr in sprite.sprites(): + if not self.has_internal(spr): + self.add_internal(spr, layer) + spr.add_internal(self) + elif not self.has_internal(sprite): + self.add_internal(sprite, layer) + sprite.add_internal(self) + + def remove_internal(self, sprite): + """Do not use this method directly. + + The group uses it to add a sprite. + + """ + self._spritelist.remove(sprite) + # these dirty rects are suboptimal for one frame + r = self.spritedict[sprite] + if r is not self._init_rect: + self.lostsprites.append(r) # dirty rect + if hasattr(sprite, 'rect'): + self.lostsprites.append(sprite.rect) # dirty rect + + del self.spritedict[sprite] + del self._spritelayers[sprite] + + def sprites(self): + """return a ordered list of sprites (first back, last top). + + LayeredUpdates.sprites(): return sprites + + """ + return list(self._spritelist) + + def draw(self, surface): + """draw all sprites in the right order onto the passed surface + + LayeredUpdates.draw(surface): return Rect_list + + """ + spritedict = self.spritedict + surface_blit = surface.blit + dirty = self.lostsprites + self.lostsprites = [] + dirty_append = dirty.append + init_rect = self._init_rect + for spr in self.sprites(): + rec = spritedict[spr] + newrect = surface_blit(spr.image, spr.rect) + if rec is init_rect: + dirty_append(newrect) + else: + if newrect.colliderect(rec): + dirty_append(newrect.union(rec)) + else: + dirty_append(newrect) + dirty_append(rec) + spritedict[spr] = newrect + return dirty + + def get_sprites_at(self, pos): + """return a list with all sprites at that position + + LayeredUpdates.get_sprites_at(pos): return colliding_sprites + + Bottom sprites are listed first; the top ones are listed last. + + """ + _sprites = self._spritelist + rect = Rect(pos, (1, 1)) + colliding_idx = rect.collidelistall(_sprites) + colliding = [_sprites[i] for i in colliding_idx] + return colliding + + def get_sprite(self, idx): + """return the sprite at the index idx from the groups sprites + + LayeredUpdates.get_sprite(idx): return sprite + + Raises IndexOutOfBounds if the idx is not within range. + + """ + return self._spritelist[idx] + + def remove_sprites_of_layer(self, layer_nr): + """remove all sprites from a layer and return them as a list + + LayeredUpdates.remove_sprites_of_layer(layer_nr): return sprites + + """ + sprites = self.get_sprites_from_layer(layer_nr) + self.remove(*sprites) + return sprites + + #---# layer methods + def layers(self): + """return a list of unique defined layers defined. + + LayeredUpdates.layers(): return layers + + """ + return sorted(set(self._spritelayers.values())) + + def change_layer(self, sprite, new_layer): + """change the layer of the sprite + + LayeredUpdates.change_layer(sprite, new_layer): return None + + The sprite must have been added to the renderer already. This is not + checked. + + """ + sprites = self._spritelist # speedup + sprites_layers = self._spritelayers # speedup + + sprites.remove(sprite) + sprites_layers.pop(sprite) + + # add the sprite at the right position + # bisect algorithmus + leng = len(sprites) + low = mid = 0 + high = leng - 1 + while low <= high: + mid = low + (high - low) // 2 + if sprites_layers[sprites[mid]] <= new_layer: + low = mid + 1 + else: + high = mid - 1 + # linear search to find final position + while mid < leng and sprites_layers[sprites[mid]] <= new_layer: + mid += 1 + sprites.insert(mid, sprite) + if hasattr(sprite, 'layer'): + sprite.layer = new_layer + + # add layer info + sprites_layers[sprite] = new_layer + + def get_layer_of_sprite(self, sprite): + """return the layer that sprite is currently in + + If the sprite is not found, then it will return the default layer. + + """ + return self._spritelayers.get(sprite, self._default_layer) + + def get_top_layer(self): + """return the top layer + + LayeredUpdates.get_top_layer(): return layer + + """ + return self._spritelayers[self._spritelist[-1]] + + def get_bottom_layer(self): + """return the bottom layer + + LayeredUpdates.get_bottom_layer(): return layer + + """ + return self._spritelayers[self._spritelist[0]] + + def move_to_front(self, sprite): + """bring the sprite to front layer + + LayeredUpdates.move_to_front(sprite): return None + + Brings the sprite to front by changing the sprite layer to the top-most + layer. The sprite is added at the end of the list of sprites in that + top-most layer. + + """ + self.change_layer(sprite, self.get_top_layer()) + + def move_to_back(self, sprite): + """move the sprite to the bottom layer + + LayeredUpdates.move_to_back(sprite): return None + + Moves the sprite to the bottom layer by moving it to a new layer below + the current bottom layer. + + """ + self.change_layer(sprite, self.get_bottom_layer() - 1) + + def get_top_sprite(self): + """return the topmost sprite + + LayeredUpdates.get_top_sprite(): return Sprite + + """ + return self._spritelist[-1] + + def get_sprites_from_layer(self, layer): + """return all sprites from a layer ordered as they where added + + LayeredUpdates.get_sprites_from_layer(layer): return sprites + + Returns all sprites from a layer. The sprites are ordered in the + sequence that they where added. (The sprites are not removed from the + layer. + + """ + sprites = [] + sprites_append = sprites.append + sprite_layers = self._spritelayers + for spr in self._spritelist: + if sprite_layers[spr] == layer: + sprites_append(spr) + elif sprite_layers[spr] > layer:# break after because no other will + # follow with same layer + break + return sprites + + def switch_layer(self, layer1_nr, layer2_nr): + """switch the sprites from layer1_nr to layer2_nr + + LayeredUpdates.switch_layer(layer1_nr, layer2_nr): return None + + The layers number must exist. This method does not check for the + existence of the given layers. + + """ + sprites1 = self.remove_sprites_of_layer(layer1_nr) + for spr in self.get_sprites_from_layer(layer2_nr): + self.change_layer(spr, layer1_nr) + self.add(layer=layer2_nr, *sprites1) + + +class LayeredDirty(LayeredUpdates): + """LayeredDirty Group is for DirtySprites; subclasses LayeredUpdates + + pygame.sprite.LayeredDirty(*spites, **kwargs): return LayeredDirty + + This group requires pygame.sprite.DirtySprite or any sprite that + has the following attributes: + image, rect, dirty, visible, blendmode (see doc of DirtySprite). + + It uses the dirty flag technique and is therefore faster than + pygame.sprite.RenderUpdates if you have many static sprites. It + also switches automatically between dirty rect updating and full + screen drawing, so you do no have to worry which would be faster. + + As with the pygame.sprite.Group, you can specify some additional attributes + through kwargs: + _use_update: True/False (default is False) + _default_layer: default layer where the sprites without a layer are + added + _time_threshold: threshold time for switching between dirty rect mode + and fullscreen mode; defaults to updating at 80 frames per second, + which is equal to 1000.0 / 80.0 + + New in pygame 1.8.0 + + """ + + def __init__(self, *sprites, **kwargs): + """initialize group. + + pygame.sprite.LayeredDirty(*spites, **kwargs): return LayeredDirty + + You can specify some additional attributes through kwargs: + _use_update: True/False (default is False) + _default_layer: default layer where the sprites without a layer are + added + _time_threshold: threshold time for switching between dirty rect + mode and fullscreen mode; defaults to updating at 80 frames per + second, which is equal to 1000.0 / 80.0 + + """ + LayeredUpdates.__init__(self, *sprites, **kwargs) + self._clip = None + + self._use_update = False + + self._time_threshold = 1000.0 / 80.0 # 1000.0 / fps + + self._bgd = None + for key, val in kwargs.items(): + if key in ['_use_update', '_time_threshold', '_default_layer']: + if hasattr(self, key): + setattr(self, key, val) + + def add_internal(self, sprite, layer=None): + """Do not use this method directly. + + It is used by the group to add a sprite internally. + + """ + # check if all needed attributes are set + if not hasattr(sprite, 'dirty'): + raise AttributeError() + if not hasattr(sprite, 'visible'): + raise AttributeError() + if not hasattr(sprite, 'blendmode'): + raise AttributeError() + + if not isinstance(sprite, DirtySprite): + raise TypeError() + + if sprite.dirty == 0: # set it dirty if it is not + sprite.dirty = 1 + + LayeredUpdates.add_internal(self, sprite, layer) + + def draw(self, surface, bgd=None): + """draw all sprites in the right order onto the given surface + + LayeredDirty.draw(surface, bgd=None): return Rect_list + + You can pass the background too. If a self.bgd is already set to some + value that is not None, then the bgd argument has no effect. + + """ + # speedups + _orig_clip = surface.get_clip() + _clip = self._clip + if _clip is None: + _clip = _orig_clip + + _surf = surface + _sprites = self._spritelist + _old_rect = self.spritedict + _update = self.lostsprites + _update_append = _update.append + _ret = None + _surf_blit = _surf.blit + _rect = Rect + if bgd is not None: + self._bgd = bgd + _bgd = self._bgd + init_rect = self._init_rect + + _surf.set_clip(_clip) + # ------- + # 0. decide whether to render with update or flip + start_time = get_ticks() + if self._use_update: # dirty rects mode + # 1. find dirty area on screen and put the rects into _update + # still not happy with that part + for spr in _sprites: + if 0 < spr.dirty: + # chose the right rect + if spr.source_rect: + _union_rect = _rect(spr.rect.topleft, + spr.source_rect.size) + else: + _union_rect = _rect(spr.rect) + + _union_rect_collidelist = _union_rect.collidelist + _union_rect_union_ip = _union_rect.union_ip + i = _union_rect_collidelist(_update) + while -1 < i: + _union_rect_union_ip(_update[i]) + del _update[i] + i = _union_rect_collidelist(_update) + _update_append(_union_rect.clip(_clip)) + + if _old_rect[spr] is not init_rect: + _union_rect = _rect(_old_rect[spr]) + _union_rect_collidelist = _union_rect.collidelist + _union_rect_union_ip = _union_rect.union_ip + i = _union_rect_collidelist(_update) + while -1 < i: + _union_rect_union_ip(_update[i]) + del _update[i] + i = _union_rect_collidelist(_update) + _update_append(_union_rect.clip(_clip)) + # can it be done better? because that is an O(n**2) algorithm in + # worst case + + # clear using background + if _bgd is not None: + for rec in _update: + _surf_blit(_bgd, rec, rec) + + # 2. draw + for spr in _sprites: + if 1 > spr.dirty: + if spr._visible: + # sprite not dirty; blit only the intersecting part + if spr.source_rect is not None: + # For possible future speed up, source_rect's data + # can be prefetched outside of this loop. + _spr_rect = _rect(spr.rect.topleft, + spr.source_rect.size) + rect_offset_x = spr.source_rect[0] - _spr_rect[0] + rect_offset_y = spr.source_rect[1] - _spr_rect[1] + else: + _spr_rect = spr.rect + rect_offset_x = -_spr_rect[0] + rect_offset_y = -_spr_rect[1] + + _spr_rect_clip = _spr_rect.clip + + for idx in _spr_rect.collidelistall(_update): + # clip + clip = _spr_rect_clip(_update[idx]) + _surf_blit(spr.image, + clip, + (clip[0] + rect_offset_x, + clip[1] + rect_offset_y, + clip[2], + clip[3]), + spr.blendmode) + else: # dirty sprite + if spr._visible: + _old_rect[spr] = _surf_blit(spr.image, + spr.rect, + spr.source_rect, + spr.blendmode) + if spr.dirty == 1: + spr.dirty = 0 + _ret = list(_update) + else: # flip, full screen mode + if _bgd is not None: + _surf_blit(_bgd, (0, 0)) + for spr in _sprites: + if spr._visible: + _old_rect[spr] = _surf_blit(spr.image, + spr.rect, + spr.source_rect, + spr.blendmode) + _ret = [_rect(_clip)] # return only the part of the screen changed + + + # timing for switching modes + # How may a good threshold be found? It depends on the hardware. + end_time = get_ticks() + if end_time-start_time > self._time_threshold: + self._use_update = False + else: + self._use_update = True + +## # debug +## print " check: using dirty rects:", self._use_update + + # emtpy dirty rects list + _update[:] = [] + + # ------- + # restore original clip + _surf.set_clip(_orig_clip) + return _ret + + def clear(self, surface, bgd): + """use to set background + + Group.clear(surface, bgd): return None + + """ + self._bgd = bgd + + def repaint_rect(self, screen_rect): + """repaint the given area + + LayeredDirty.repaint_rect(screen_rect): return None + + screen_rect is in screen coordinates. + + """ + if self._clip: + self.lostsprites.append(screen_rect.clip(self._clip)) + else: + self.lostsprites.append(Rect(screen_rect)) + + def set_clip(self, screen_rect=None): + """clip the area where to draw; pass None (default) to reset the clip + + LayeredDirty.set_clip(screen_rect=None): return None + + """ + if screen_rect is None: + self._clip = pygame.display.get_surface().get_rect() + else: + self._clip = screen_rect + self._use_update = False + + def get_clip(self): + """get the area where drawing will occur + + LayeredDirty.get_clip(): return Rect + + """ + return self._clip + + def change_layer(self, sprite, new_layer): + """change the layer of the sprite + + LayeredUpdates.change_layer(sprite, new_layer): return None + + The sprite must have been added to the renderer already. This is not + checked. + + """ + LayeredUpdates.change_layer(self, sprite, new_layer) + if sprite.dirty == 0: + sprite.dirty = 1 + + def set_timing_treshold(self, time_ms): + """set the threshold in milliseconds + + set_timing_treshold(time_ms): return None + + Defaults to 1000.0 / 80.0. This means that the screen will be painted + using the flip method rather than the update method if the update + method is taking so long to update the screen that the frame rate falls + below 80 frames per second. + + Raises TypeError if time_ms is not int or float. + + """ + if isinstance(time_ms, (int, float)): + self._time_threshold = time_ms + else: + raise TypeError("Expected numeric value, got {} instead". + format(time_ms.__class__.__name__)) + + +class GroupSingle(AbstractGroup): + """A group container that holds a single most recent item. + + This class works just like a regular group, but it only keeps a single + sprite in the group. Whatever sprite has been added to the group last will + be the only sprite in the group. + + You can access its one sprite as the .sprite attribute. Assigning to this + attribute will properly remove the old sprite and then add the new one. + + """ + + def __init__(self, sprite=None): + AbstractGroup.__init__(self) + self.__sprite = None + if sprite is not None: + self.add(sprite) + + def copy(self): + return GroupSingle(self.__sprite) + + def sprites(self): + if self.__sprite is not None: + return [self.__sprite] + else: + return [] + + def add_internal(self, sprite): + if self.__sprite is not None: + self.__sprite.remove_internal(self) + self.remove_internal(self.__sprite) + self.__sprite = sprite + + def __nonzero__(self): + return self.__sprite is not None + + def _get_sprite(self): + return self.__sprite + + def _set_sprite(self, sprite): + self.add_internal(sprite) + sprite.add_internal(self) + return sprite + + sprite = property(_get_sprite, + _set_sprite, + None, + "The sprite contained in this group") + + def remove_internal(self, sprite): + if sprite is self.__sprite: + self.__sprite = None + if sprite in self.spritedict: + AbstractGroup.remove_internal(self, sprite) + + def has_internal(self, sprite): + return self.__sprite is sprite + + # Optimizations... + def __contains__(self, sprite): + return self.__sprite is sprite + + +# Some different collision detection functions that could be used. +def collide_rect(left, right): + """collision detection between two sprites, using rects. + + pygame.sprite.collide_rect(left, right): return bool + + Tests for collision between two sprites. Uses the pygame.Rect colliderect + function to calculate the collision. It is intended to be passed as a + collided callback function to the *collide functions. Sprites must have + "rect" attributes. + + New in pygame 1.8.0 + + """ + return left.rect.colliderect(right.rect) + +class collide_rect_ratio: + """A callable class that checks for collisions using scaled rects + + The class checks for collisions between two sprites using a scaled version + of the sprites' rects. Is created with a ratio; the instance is then + intended to be passed as a collided callback function to the *collide + functions. + + New in pygame 1.8.1 + + """ + + def __init__(self, ratio): + """create a new collide_rect_ratio callable + + Ratio is expected to be a floating point value used to scale + the underlying sprite rect before checking for collisions. + + """ + self.ratio = ratio + + def __call__(self, left, right): + """detect collision between two sprites using scaled rects + + pygame.sprite.collide_rect_ratio(ratio)(left, right): return bool + + Tests for collision between two sprites. Uses the pygame.Rect + colliderect function to calculate the collision after scaling the rects + by the stored ratio. Sprites must have "rect" attributes. + + """ + + ratio = self.ratio + + leftrect = left.rect + width = leftrect.width + height = leftrect.height + leftrect = leftrect.inflate(width * ratio - width, + height * ratio - height) + + rightrect = right.rect + width = rightrect.width + height = rightrect.height + rightrect = rightrect.inflate(width * ratio - width, + height * ratio - height) + + return leftrect.colliderect(rightrect) + +def collide_circle(left, right): + """detect collision between two sprites using circles + + pygame.sprite.collide_circle(left, right): return bool + + Tests for collision between two sprites by testing whether two circles + centered on the sprites overlap. If the sprites have a "radius" attribute, + then that radius is used to create the circle; otherwise, a circle is + created that is big enough to completely enclose the sprite's rect as + given by the "rect" attribute. This function is intended to be passed as + a collided callback function to the *collide functions. Sprites must have a + "rect" and an optional "radius" attribute. + + New in pygame 1.8.0 + + """ + + xdistance = left.rect.centerx - right.rect.centerx + ydistance = left.rect.centery - right.rect.centery + distancesquared = xdistance ** 2 + ydistance ** 2 + + if hasattr(left, 'radius'): + leftradius = left.radius + else: + leftrect = left.rect + # approximating the radius of a square by using half of the diagonal, + # might give false positives (especially if its a long small rect) + leftradius = 0.5 * ((leftrect.width ** 2 + leftrect.height ** 2) ** 0.5) + # store the radius on the sprite for next time + setattr(left, 'radius', leftradius) + + if hasattr(right, 'radius'): + rightradius = right.radius + else: + rightrect = right.rect + # approximating the radius of a square by using half of the diagonal + # might give false positives (especially if its a long small rect) + rightradius = 0.5 * ((rightrect.width ** 2 + rightrect.height ** 2) ** 0.5) + # store the radius on the sprite for next time + setattr(right, 'radius', rightradius) + return distancesquared <= (leftradius + rightradius) ** 2 + +class collide_circle_ratio(object): + """detect collision between two sprites using scaled circles + + This callable class checks for collisions between two sprites using a + scaled version of a sprite's radius. It is created with a ratio as the + argument to the constructor. The instance is then intended to be passed as + a collided callback function to the *collide functions. + + New in pygame 1.8.1 + + """ + + def __init__(self, ratio): + """creates a new collide_circle_ratio callable instance + + The given ratio is expected to be a floating point value used to scale + the underlying sprite radius before checking for collisions. + + When the ratio is ratio=1.0, then it behaves exactly like the + collide_circle method. + + """ + self.ratio = ratio + + + def __call__(self, left, right): + """detect collision between two sprites using scaled circles + + pygame.sprite.collide_circle_radio(ratio)(left, right): return bool + + Tests for collision between two sprites by testing whether two circles + centered on the sprites overlap after scaling the circle's radius by + the stored ratio. If the sprites have a "radius" attribute, that is + used to create the circle; otherwise, a circle is created that is big + enough to completely enclose the sprite's rect as given by the "rect" + attribute. Intended to be passed as a collided callback function to the + *collide functions. Sprites must have a "rect" and an optional "radius" + attribute. + + """ + + ratio = self.ratio + xdistance = left.rect.centerx - right.rect.centerx + ydistance = left.rect.centery - right.rect.centery + distancesquared = xdistance ** 2 + ydistance ** 2 + + if hasattr(left, "radius"): + leftradius = left.radius * ratio + else: + leftrect = left.rect + leftradius = ratio * 0.5 * ((leftrect.width ** 2 + leftrect.height ** 2) ** 0.5) + # store the radius on the sprite for next time + setattr(left, 'radius', leftradius) + + if hasattr(right, "radius"): + rightradius = right.radius * ratio + else: + rightrect = right.rect + rightradius = ratio * 0.5 * ((rightrect.width ** 2 + rightrect.height ** 2) ** 0.5) + # store the radius on the sprite for next time + setattr(right, 'radius', rightradius) + + return distancesquared <= (leftradius + rightradius) ** 2 + +def collide_mask(left, right): + """collision detection between two sprites, using masks. + + pygame.sprite.collide_mask(SpriteLeft, SpriteRight): bool + + Tests for collision between two sprites by testing if their bitmasks + overlap. If the sprites have a "mask" attribute, that is used as the mask; + otherwise, a mask is created from the sprite image. Intended to be passed + as a collided callback function to the *collide functions. Sprites must + have a "rect" and an optional "mask" attribute. + + New in pygame 1.8.0 + + """ + xoffset = right.rect[0] - left.rect[0] + yoffset = right.rect[1] - left.rect[1] + try: + leftmask = left.mask + except AttributeError: + leftmask = from_surface(left.image) + try: + rightmask = right.mask + except AttributeError: + rightmask = from_surface(right.image) + return leftmask.overlap(rightmask, (xoffset, yoffset)) + +def spritecollide(sprite, group, dokill, collided=None): + """find Sprites in a Group that intersect another Sprite + + pygame.sprite.spritecollide(sprite, group, dokill, collided=None): + return Sprite_list + + Return a list containing all Sprites in a Group that intersect with another + Sprite. Intersection is determined by comparing the Sprite.rect attribute + of each Sprite. + + The dokill argument is a bool. If set to True, all Sprites that collide + will be removed from the Group. + + The collided argument is a callback function used to calculate if two + sprites are colliding. it should take two sprites as values, and return a + bool value indicating if they are colliding. If collided is not passed, all + sprites must have a "rect" value, which is a rectangle of the sprite area, + which will be used to calculate the collision. + + """ + if dokill: + + crashed = [] + append = crashed.append + + if collided: + for s in group.sprites(): + if collided(sprite, s): + s.kill() + append(s) + else: + spritecollide = sprite.rect.colliderect + for s in group.sprites(): + if spritecollide(s.rect): + s.kill() + append(s) + + return crashed + + elif collided: + return [s for s in group if collided(sprite, s)] + else: + spritecollide = sprite.rect.colliderect + return [s for s in group if spritecollide(s.rect)] + + +def groupcollide(groupa, groupb, dokilla, dokillb, collided=None): + """detect collision between a group and another group + + pygame.sprite.groupcollide(groupa, groupb, dokilla, dokillb): + return dict + + Given two groups, this will find the intersections between all sprites in + each group. It returns a dictionary of all sprites in the first group that + collide. The value for each item in the dictionary is a list of the sprites + in the second group it collides with. The two dokill arguments control if + the sprites from either group will be automatically removed from all + groups. Collided is a callback function used to calculate if two sprites + are colliding. it should take two sprites as values, and return a bool + value indicating if they are colliding. If collided is not passed, all + sprites must have a "rect" value, which is a rectangle of the sprite area + that will be used to calculate the collision. + + """ + crashed = {} + SC = spritecollide + if dokilla: + for s in groupa.sprites(): + c = SC(s, groupb, dokillb, collided) + if c: + crashed[s] = c + s.kill() + else: + for s in groupa: + c = SC(s, groupb, dokillb, collided) + if c: + crashed[s] = c + return crashed + +def spritecollideany(sprite, group, collided=None): + """finds any sprites in a group that collide with the given sprite + + pygame.sprite.spritecollideany(sprite, group): return sprite + + Given a sprite and a group of sprites, this will return return any single + sprite that collides with with the given sprite. If there are no + collisions, then this returns None. + + If you don't need all the features of the spritecollide function, this + function will be a bit quicker. + + Collided is a callback function used to calculate if two sprites are + colliding. It should take two sprites as values and return a bool value + indicating if they are colliding. If collided is not passed, then all + sprites must have a "rect" value, which is a rectangle of the sprite area, + which will be used to calculate the collision. + + + """ + if collided: + for s in group: + if collided(sprite, s): + return s + else: + # Special case old behaviour for speed. + spritecollide = sprite.rect.colliderect + for s in group: + if spritecollide(s.rect): + return s + return None diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/sprite.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/sprite.pyi new file mode 100644 index 0000000..45817ad --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/sprite.pyi @@ -0,0 +1,141 @@ +from typing import List, Dict, Any, Union, Tuple, Optional, Callable, SupportsFloat + +from pygame.rect import Rect +from pygame.surface import Surface + +_RectStyle = Union[ + Tuple[float, float, float, float], + Tuple[Tuple[float, float], Tuple[float, float]], + List[float], + Rect, +] + +# Some functions violate Liskov substitution principle so mypy will throw errors for this file, but this are the +# best type hints I could do + +class Sprite: + image: Surface + rect: Rect + def __init__(self, *groups: Group) -> None: ... + def update(self, *args, **kwargs) -> None: ... + def add(self, *groups: Group) -> None: ... + def remove(self, *groups: Group) -> None: ... + def kill(self) -> None: ... + def alive(self) -> bool: ... + def groups(self) -> List[Group]: ... + +class DirtySprite(Sprite): + dirty: int + blendmode: int + source_rect: Rect + visible: int + layer: int + def _set_visible(self, value: int) -> None: ... + def _get_visible(self) -> int: ... + +class AbstractGroup: + spritedict = Dict[Sprite, int] + lostsprites = List[int] # I think + def __init__(self) -> None: ... + def copy(self) -> AbstractGroup: ... + def sprites(self) -> List[Sprite]: ... + def add(self, *sprites: Sprite) -> None: ... + def remove(self, *sprites: Sprite) -> None: ... + def has(self, *sprites: Sprite) -> bool: ... + def update(self, *args, **kwargs) -> None: ... + def draw(self, surface: Surface) -> None: ... + def clear(self, surface_dest: Surface, background: Surface) -> None: ... + def empty(self) -> None: ... + +class Group(AbstractGroup): + def __init__(self, *sprites: Sprite) -> None: + AbstractGroup.__init__(self) + def copy(self) -> Group: ... + +class RenderPlain(Group): + def copy(self) -> RenderPlain: ... + +class RenderClear(Group): + def copy(self) -> RenderClear: ... + +class RenderUpdates(Group): + def copy(self) -> RenderUpdates: ... + def draw(self, surface: Surface) -> List[Rect]: ... + +class OrderedUpdates(RenderUpdates): + def copy(self) -> OrderedUpdates: ... + +class LayeredUpdates(AbstractGroup): + def __init__(self, *sprites: Sprite, **kwargs: Dict[str, Any]) -> None: + AbstractGroup.__init__(self) + def copy(self) -> LayeredUpdates: ... + def add(self, *sprites: Sprite, **kwargs: Dict[str, Any]) -> None: ... + def draw(self, surface: Surface) -> List[Rect]: ... + def get_sprites_at( + self, pos: Union[Tuple[int, int], List[int]] + ) -> List[Sprite]: ... + def get_sprite(self, idx: int) -> Sprite: ... + def remove_sprites_of_layer(self, layer_nr: int) -> List[Sprite]: ... + def layers(self) -> List[int]: ... + def change_layer(self, sprite: Sprite, new_layer: int) -> None: ... + def get_layer_of_sprite(self, sprite: Sprite) -> int: ... + def get_top_layer(self) -> int: ... + def get_bottom_layer(self) -> int: ... + def move_to_front(self, sprite: Sprite) -> None: ... + def move_to_back(self, sprite: Sprite) -> None: ... + def get_top_sprite(self) -> Sprite: ... + def get_sprites_from_layer(self, layer: int) -> List[Sprite]: ... + def switch_layer(self, layer1_nr, layer2_nr) -> None: ... + +class LayeredDirty(LayeredUpdates): + def __init__(self, *sprites: DirtySprite, **kwargs: Dict[str, Any]): + LayeredUpdates.__init__(self, *sprites, **kwargs) + def copy(self) -> LayeredDirty: ... + def draw(self, surface: Surface, bgd: Optional[Surface] = None) -> List[Rect]: ... + def clear(self, surface: Surface, bgd: Surface) -> None: ... + def repaint_rect(self, screen_rect: _RectStyle) -> None: ... + def set_clip(self, screen_rect: Optional[_RectStyle] = None): ... + def get_clip(self) -> Rect: ... + def set_timing_treshold( + self, time_ms: SupportsFloat + ) -> None: ... # This actually accept any value + +class GroupSingle(AbstractGroup): + sprite: Sprite + def __init__(self, sprite: Optional[Sprite]): + AbstractGroup.__init__(self) + def copy(self) -> GroupSingle: ... + +def spritecollide( + sprite: Sprite, + group: AbstractGroup, + dokill: bool, + collided: Optional[Callable[[Sprite, Sprite], bool]] = None, +) -> List[Sprite]: ... +def collide_rect(left: Sprite, right: Sprite) -> bool: ... + +class collide_rect_ratio: + ratio: float + def __init__(self, ratio: float) -> None: ... + def __call__(self, left: Sprite, right: Sprite) -> bool: ... + +def collide_circle(left: Sprite, right: Sprite) -> bool: ... + +class collide_circle_ratio: + ratio: float + def __init__(self, ratio: float): ... + def __call__(self, left: Sprite, right: Sprite) -> bool: ... + +def collide_mask(sprite1: Sprite, sprite2: Sprite) -> Tuple[int, int]: ... +def groupcollide( + group1: AbstractGroup, + group2: AbstractGroup, + dokill: bool, + dokill2: bool, + collided: Optional[Callable[[Sprite, Sprite], bool]] = None, +) -> Dict[Sprite, Sprite]: ... +def spritecollideany( + sprite: Sprite, + group: AbstractGroup, + collided: Optional[Callable[[Sprite, Sprite], bool]] = None, +) -> Sprite: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/surface.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/surface.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..3c1d4e7 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/surface.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/surface.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/surface.pyi new file mode 100644 index 0000000..91c9b4a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/surface.pyi @@ -0,0 +1,125 @@ +from typing import Any, List, Optional, Sequence, Text, Tuple, Union, overload, Iterable +from pygame.bufferproxy import BufferProxy +from pygame.color import Color +from pygame.rect import Rect + +from pygame.math import Vector2 + +_ColorInput = Union[ + Color, str, List[int], Tuple[int, int, int], Tuple[int, int, int, int] +] +_RgbaOutput = Tuple[int, int, int, int] +_RectStyle = Union[ + Tuple[float, float, float, float], + Tuple[Tuple[float, float], Tuple[float, float]], + List[float], + List[Vector2], + Tuple[Vector2, Vector2], + Iterable[Vector2], +] + +class Surface(object): + _pixels_address: int + @overload + def __init__( + self, + width_height: Tuple[float, float], + flags: int = ..., + depth: int = ..., + masks: Optional[_ColorInput] = ..., + ) -> None: ... + @overload + def __init__( + self, + width_height: Tuple[float, float], + flags: int = ..., + surface: Surface = ..., + ) -> None: ... + def blit( + self, + source: Surface, + dest: Union[Sequence[float], Rect], + area: Optional[Rect] = ..., + special_flags: int = ..., + ) -> Rect: ... + def blits( + self, sequence: Sequence[Union[Surface, Rect]], doreturn: Union[int, bool] + ) -> Union[List[Rect], None]: ... + @overload + def convert(self, surface: Surface) -> Surface: ... + @overload + def convert(self, depth: int, flags: int = ...) -> Surface: ... + @overload + def convert(self, masks: _ColorInput, flags: int = ...) -> Surface: ... + @overload + def convert(self) -> Surface: ... + @overload + def convert_alpha(self, surface: Surface) -> Surface: ... + @overload + def convert_alpha(self) -> Surface: ... + def copy(self) -> Surface: ... + def fill( + self, + color: _ColorInput, + rect: Optional[_RectStyle] = ..., + special_flags: int = ..., + ) -> Rect: ... + def scroll(self, dx: int = ..., dy: int = ...) -> None: ... + @overload + def set_colorkey(self, color: _ColorInput, flags: int = ...) -> None: ... + @overload + def set_colorkey(self, color: None) -> None: ... + def get_colorkey(self) -> Optional[_RgbaOutput]: ... + @overload + def set_alpha(self, value: int, flags: int = ...) -> None: ... + @overload + def set_alpha(self, value: None) -> None: ... + def get_alpha(self) -> Optional[int]: ... + def lock(self) -> None: ... + def unlock(self) -> None: ... + def mustlock(self) -> bool: ... + def get_locked(self) -> bool: ... + def get_locks(self) -> Tuple[Any, ...]: ... + def get_at(self, x_y: Sequence[int]) -> _RgbaOutput: ... + def set_at(self, x_y: Sequence[int], color: _ColorInput) -> None: ... + def get_at_mapped(self, x_y: Sequence[int]) -> int: ... + def get_palette(self) -> List[_RgbaOutput]: ... + def get_palette_at(self, index: int) -> _RgbaOutput: ... + def set_palette(self, palette: List[_ColorInput]) -> None: ... + def set_palette_at(self, index: int, color: _ColorInput) -> None: ... + def map_rgb(self, color: _ColorInput) -> int: ... + def unmap_rgb(self, mapped_int: int) -> _RgbaOutput: ... + def set_clip(self, rect: Optional[Rect]) -> None: ... + def get_clip(self) -> Rect: ... + @overload + def subsurface(self, rect: Union[_RectStyle, Rect]) -> Surface: ... + @overload + def subsurface( + self, + left_top: Union[List[float], Tuple[float, float], Vector2], + width_height: Union[List[float], Tuple[float, float], Vector2], + ) -> Surface: ... + @overload + def subsurface( + self, left: float, top: float, width: float, height: float + ) -> Surface: ... + def get_parent(self) -> Surface: ... + def get_abs_parent(self) -> Surface: ... + def get_offset(self) -> Tuple[int, int]: ... + def get_abs_offset(self) -> Tuple[int, int]: ... + def get_size(self) -> Tuple[int, int]: ... + def get_width(self) -> int: ... + def get_height(self) -> int: ... + def get_rect(self, **kwargs) -> Rect: ... + def get_bitsize(self) -> int: ... + def get_bytesize(self) -> int: ... + def get_flags(self) -> int: ... + def get_pitch(self) -> int: ... + def get_masks(self) -> _RgbaOutput: ... + def set_masks(self, color: _ColorInput) -> None: ... + def get_shifts(self) -> _RgbaOutput: ... + def set_shifts(self, color: _ColorInput) -> None: ... + def get_losses(self) -> _RgbaOutput: ... + def get_bounding_rect(self, min_alpha: int = ...) -> Rect: ... + def get_view(self, kind: Text = ...) -> BufferProxy: ... + def get_buffer(self) -> BufferProxy: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/surfarray.py b/Display/.venv/lib/python3.7/site-packages/pygame/surfarray.py new file mode 100644 index 0000000..8a99fe7 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/surfarray.py @@ -0,0 +1,372 @@ +## pygame - Python Game Library +## Copyright (C) 2007 Marcus von Appen +## +## This library is free software; you can redistribute it and/or +## modify it under the terms of the GNU Library General Public +## License as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## Library General Public License for more details. +## +## You should have received a copy of the GNU Library General Public +## License along with this library; if not, write to the Free +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## +## Marcus von Appen +## mva@sysfault.org + +"""pygame module for accessing surface pixel data using array interfaces + +Functions to convert pixel data between pygame Surfaces and arrays. This +module will only be functional when pygame can use the external Numpy or +Numeric packages. + +Every pixel is stored as a single integer value to represent the red, +green, and blue colors. The 8bit images use a value that looks into a +colormap. Pixels with higher depth use a bit packing process to place +three or four values into a single number. + +The arrays are indexed by the X axis first, followed by the Y +axis. Arrays that treat the pixels as a single integer are referred to +as 2D arrays. This module can also separate the red, green, and blue +color values into separate indices. These types of arrays are referred +to as 3D arrays, and the last index is 0 for red, 1 for green, and 2 for +blue. + +Supported array types are + + numpy + numeric (deprecated; will be removed in Pygame 1.9.3.) + +The default will be numpy, if installed. Otherwise, Numeric will be set +as default if installed, and a deprecation warning will be issued. If +neither numpy nor Numeric are installed, the module will raise an +ImportError. + +The array type to use can be changed at runtime using the use_arraytype() +method, which requires one of the above types as string. + +Note: numpy and Numeric are not completely compatible. Certain array +manipulations, which work for one type, might behave differently or even +completely break for the other. + +Additionally, in contrast to Numeric, numpy does use unsigned 16-bit +integers. Images with 16-bit data will be treated as unsigned +integers. Numeric instead uses signed integers for the representation, +which is important to keep in mind, if you use the module's functions +and wonder about the values. +""" + +# Try to import the necessary modules. +# import pygame._numpysurfarray as numpysf +numpysf = None + + +from pygame.pixelcopy import array_to_surface, make_surface as pc_make_surface + +__all__ = ["array_to_surface", "pc_make_surface"] + +def blit_array (surface, array): + """pygame.surfarray.blit_array(Surface, array): return None + + Blit directly from a array values. + + Directly copy values from an array into a Surface. This is faster than + converting the array into a Surface and blitting. The array must be the + same dimensions as the Surface and will completely replace all pixel + values. Only integer, ascii character and record arrays are accepted. + + This function will temporarily lock the Surface as the new values are + copied. + """ + global numpysf + try: + return numpysf.blit_array (surface, array) + except AttributeError: + import pygame._numpysurfarray as numpysf + return numpysf.blit_array (surface, array) + + +def array2d (surface): + """pygame.surfarray.array2d (Surface): return array + + Copy pixels into a 2d array. + + Copy the pixels from a Surface into a 2D array. The bit depth of the + surface will control the size of the integer values, and will work + for any type of pixel format. + + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + global numpysf + try: + return numpysf.array2d (surface) + except AttributeError: + import pygame._numpysurfarray as numpysf + return numpysf.array2d (surface) + + +def pixels2d (surface): + """pygame.surfarray.pixels2d (Surface): return array + + Reference pixels into a 2d array. + + Create a new 2D array that directly references the pixel values in a + Surface. Any changes to the array will affect the pixels in the + Surface. This is a fast operation since no data is copied. + + Pixels from a 24-bit Surface cannot be referenced, but all other + Surface bit depths can. + + The Surface this references will remain locked for the lifetime of + the array (see the Surface.lock - lock the Surface memory for pixel + access method). + """ + global numpysf + try: + return numpysf.pixels2d(surface) + except AttributeError: + import pygame._numpysurfarray as numpysf + return numpysf.pixels2d(surface) + + +def array3d (surface): + """pygame.surfarray.array3d (Surface): return array + + Copy pixels into a 3d array. + + Copy the pixels from a Surface into a 3D array. The bit depth of the + surface will control the size of the integer values, and will work + for any type of pixel format. + + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + global numpysf + try: + return numpysf.array3d(surface) + except AttributeError: + import pygame._numpysurfarray as numpysf + return numpysf.array3d(surface) + + +def pixels3d (surface): + """pygame.surfarray.pixels3d (Surface): return array + + Reference pixels into a 3d array. + + Create a new 3D array that directly references the pixel values in a + Surface. Any changes to the array will affect the pixels in the + Surface. This is a fast operation since no data is copied. + + This will only work on Surfaces that have 24-bit or 32-bit + formats. Lower pixel formats cannot be referenced. + + The Surface this references will remain locked for the lifetime of + the array (see the Surface.lock - lock the Surface memory for pixel + access method). + """ + global numpysf + try: + return numpysf.pixels3d(surface) + except AttributeError: + import pygame._numpysurfarray as numpysf + return numpysf.pixels3d(surface) + + +def array_alpha (surface): + """pygame.surfarray.array_alpha (Surface): return array + + Copy pixel alphas into a 2d array. + + Copy the pixel alpha values (degree of transparency) from a Surface + into a 2D array. This will work for any type of Surface + format. Surfaces without a pixel alpha will return an array with all + opaque values. + + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + global numpysf + try: + return numpysf.array_alpha(surface) + except AttributeError: + import pygame._numpysurfarray as numpysf + return numpysf.array_alpha(surface) + + +def pixels_alpha (surface): + """pygame.surfarray.pixels_alpha (Surface): return array + + Reference pixel alphas into a 2d array. + + Create a new 2D array that directly references the alpha values + (degree of transparency) in a Surface. Any changes to the array will + affect the pixels in the Surface. This is a fast operation since no + data is copied. + + This can only work on 32-bit Surfaces with a per-pixel alpha value. + + The Surface this array references will remain locked for the + lifetime of the array. + """ + global numpysf + try: + return numpysf.pixels_alpha(surface) + except AttributeError: + import pygame._numpysurfarray as numpysf + return numpysf.pixels_alpha(surface) + + +def pixels_red (surface): + """pygame.surfarray.pixels_red (Surface): return array + + Reference pixel red into a 2d array. + + Create a new 2D array that directly references the red values + in a Surface. Any changes to the array will affect the pixels + in the Surface. This is a fast operation since no data is copied. + + This can only work on 24-bit or 32-bit Surfaces. + + The Surface this array references will remain locked for the + lifetime of the array. + """ + global numpysf + try: + return numpysf.pixels_red(surface) + except AttributeError: + import pygame._numpysurfarray as numpysf + return numpysf.pixels_red(surface) + + +def pixels_green (surface): + """pygame.surfarray.pixels_green (Surface): return array + + Reference pixel green into a 2d array. + + Create a new 2D array that directly references the green values + in a Surface. Any changes to the array will affect the pixels + in the Surface. This is a fast operation since no data is copied. + + This can only work on 24-bit or 32-bit Surfaces. + + The Surface this array references will remain locked for the + lifetime of the array. + """ + global numpysf + try: + return numpysf.pixels_green(surface) + except AttributeError: + import pygame._numpysurfarray as numpysf + return numpysf.pixels_green(surface) + + +def pixels_blue (surface): + """pygame.surfarray.pixels_blue (Surface): return array + + Reference pixel blue into a 2d array. + + Create a new 2D array that directly references the blue values + in a Surface. Any changes to the array will affect the pixels + in the Surface. This is a fast operation since no data is copied. + + This can only work on 24-bit or 32-bit Surfaces. + + The Surface this array references will remain locked for the + lifetime of the array. + """ + global numpysf + try: + return numpysf.pixels_blue(surface) + except AttributeError: + import pygame._numpysurfarray as numpysf + return numpysf.pixels_blue(surface) + + +def array_colorkey (surface): + """pygame.surfarray.array_colorkey (Surface): return array + + Copy the colorkey values into a 2d array. + + Create a new array with the colorkey transparency value from each + pixel. If the pixel matches the colorkey it will be fully + tranparent; otherwise it will be fully opaque. + + This will work on any type of Surface format. If the image has no + colorkey a solid opaque array will be returned. + + This function will temporarily lock the Surface as pixels are + copied. + """ + global numpysf + try: + return numpysf.array_colorkey(surface) + except AttributeError: + import pygame._numpysurfarray as numpysf + return numpysf.array_colorkey(surface) + + +def make_surface(array): + """pygame.surfarray.make_surface (array): return Surface + + Copy an array to a new surface. + + Create a new Surface that best resembles the data and format on the + array. The array can be 2D or 3D with any sized integer values. + """ + global numpysf + try: + return numpysf.make_surface(array) + except AttributeError: + import pygame._numpysurfarray as numpysf + return numpysf.make_surface(array) + + +def map_array (surface, array): + """pygame.surfarray.map_array (Surface, array3d): return array2d + + Map a 3D array into a 2D array. + + Convert a 3D array into a 2D array. This will use the given Surface + format to control the conversion. Palette surface formats are not + supported. + """ + global numpysf + try: + return numpysf.map_array(surface, array) + except AttributeError: + import pygame._numpysurfarray as numpysf + return numpysf.map_array(surface, array) + + +def use_arraytype (arraytype): + """pygame.surfarray.use_arraytype (arraytype): return None + + DEPRECATED - only numpy arrays are now supported. + """ + arraytype = arraytype.lower () + if arraytype != "numpy": + raise ValueError("invalid array type") + +def get_arraytype (): + """pygame.surfarray.get_arraytype (): return str + + DEPRECATED - only numpy arrays are now supported. + """ + return "numpy" + +def get_arraytypes (): + """pygame.surfarray.get_arraytypes (): return tuple + + DEPRECATED - only numpy arrays are now supported. + """ + return ("numpy",) + diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/surfarray.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/surfarray.pyi new file mode 100644 index 0000000..85c3353 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/surfarray.pyi @@ -0,0 +1,20 @@ +from typing import Tuple +from pygame.surface import Surface +import numpy + +def array2d(surface: Surface) -> numpy.ndarray: ... +def pixels2d(surface: Surface) -> numpy.ndarray: ... +def array3d(surface: Surface) -> numpy.ndarray: ... +def pixels3d(surface: Surface) -> numpy.ndarray: ... +def array_alpha(surface: Surface) -> numpy.ndarray: ... +def pixels_alpha(surface: Surface) -> numpy.ndarray: ... +def pixels_red(surface: Surface) -> numpy.ndarray: ... +def pixels_green(surface: Surface) -> numpy.ndarray: ... +def pixels_blue(surface: Surface) -> numpy.ndarray: ... +def array_colorkey(surface: Surface) -> numpy.ndarray: ... +def make_surface(array: numpy.ndarray) -> Surface: ... +def blit_array(surface: Surface, array: numpy.ndarray) -> None: ... +def map_array(surface: Surface, array3d: numpy.ndarray) -> numpy.ndarray: ... +def use_arraytype(arraytype: str) -> None: ... +def get_arraytype() -> str: ... +def get_arraytypes() -> Tuple[str]: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/surflock.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/surflock.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..22024f9 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/surflock.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/sysfont.py b/Display/.venv/lib/python3.7/site-packages/pygame/sysfont.py new file mode 100644 index 0000000..1bcff71 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/sysfont.py @@ -0,0 +1,412 @@ +# coding: ascii +# pygame - Python Game Library +# Copyright (C) 2000-2003 Pete Shinners +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Pete Shinners +# pete@shinners.org +"""sysfont, used in the font module to find system fonts""" + +import os +import sys +from pygame.compat import xrange_, PY_MAJOR_VERSION +from os.path import basename, dirname, exists, join, splitext +if sys.platform == 'darwin': + import xml.etree.ElementTree as ET + + +OpenType_extensions = frozenset(('.ttf', '.ttc', '.otf')) +Sysfonts = {} +Sysalias = {} + +# Python 3 compatibility +if PY_MAJOR_VERSION >= 3: + def toascii(raw): + """convert bytes to ASCII-only string""" + return raw.decode('ascii', 'ignore') + if os.name == 'nt': + import winreg as _winreg + else: + import subprocess +else: + def toascii(raw): + """return ASCII characters of a given unicode or 8-bit string""" + return raw.decode('ascii', 'ignore') + if os.name == 'nt': + import _winreg + else: + import subprocess + + +def _simplename(name): + """create simple version of the font name""" + # return alphanumeric characters of a string (converted to lowercase) + return ''.join(c.lower() for c in name if c.isalnum()) + + +def _addfont(name, bold, italic, font, fontdict): + """insert a font and style into the font dictionary""" + if name not in fontdict: + fontdict[name] = {} + fontdict[name][bold, italic] = font + + +def initsysfonts_win32(): + """initialize fonts dictionary on Windows""" + + fontdir = join(os.environ.get('WINDIR', 'C:\\Windows'), 'Fonts') + + TrueType_suffix = '(TrueType)' + mods = ('demibold', 'narrow', 'light', 'unicode', 'bt', 'mt') + + fonts = {} + + # add fonts entered in the registry + + # find valid registry keys containing font information. + # http://docs.python.org/lib/module-sys.html + # 0 (VER_PLATFORM_WIN32s) Win32s on Windows 3.1 + # 1 (VER_PLATFORM_WIN32_WINDOWS) Windows 95/98/ME + # 2 (VER_PLATFORM_WIN32_NT) Windows NT/2000/XP + # 3 (VER_PLATFORM_WIN32_CE) Windows CE + if sys.getwindowsversion()[0] == 1: + key_name = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts" + else: + key_name = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts" + key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, key_name) + + for i in xrange_(_winreg.QueryInfoKey(key)[1]): + try: + # name is the font's name e.g. Times New Roman (TrueType) + # font is the font's filename e.g. times.ttf + name, font = _winreg.EnumValue(key, i)[0:2] + except EnvironmentError: + break + + # try to handle windows unicode strings for file names with + # international characters + if PY_MAJOR_VERSION < 3: + # here are two documents with some information about it: + # http://www.python.org/peps/pep-0277.html + # https://www.microsoft.com/technet/archive/interopmigration/linux/mvc/lintowin.mspx#ECAA + try: + font = str(font) + except UnicodeEncodeError: + # MBCS is the windows encoding for unicode file names. + try: + font = font.encode('MBCS') + except UnicodeEncodeError: + # no success with str or MBCS encoding... skip this font. + continue + + if splitext(font)[1].lower() not in OpenType_extensions: + continue + if not dirname(font): + font = join(fontdir, font) + + if name.endswith(TrueType_suffix): + name = name.rstrip(TrueType_suffix).rstrip() + name = name.lower().split() + + bold = italic = 0 + for m in mods: + if m in name: + name.remove(m) + if 'bold' in name: + name.remove('bold') + bold = 1 + if 'italic' in name: + name.remove('italic') + italic = 1 + name = ''.join(name) + + name = _simplename(name) + + _addfont(name, bold, italic, font, fonts) + + return fonts + + +def _add_font_paths(sub_elements, fonts): + """ Gets each element, checks its tag content, + if wanted fetches the next value in the iterable + """ + font_name = font_path = None + for tag in sub_elements: + if tag.text == "_name": + font_name = next(sub_elements).text + if splitext(font_name)[1] not in OpenType_extensions: + break + bold = "bold" in font_name + italic = "italic" in font_name + if tag.text == "path" and font_name is not None: + font_path = next(sub_elements).text + _addfont(_simplename(font_name),bold,italic,font_path,fonts) + break + + +def _system_profiler_darwin(): + fonts = {} + flout, flerr = subprocess.Popen( + ' '.join(['system_profiler', '-xml','SPFontsDataType']), + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=True + ).communicate() + + for font_node in ET.fromstring(flout).iterfind('./array/dict/array/dict'): + _add_font_paths(font_node.iter("*"), fonts) + + return fonts + + + +def initsysfonts_darwin(): + """ Read the fonts on MacOS, and OS X. + """ + # if the X11 binary exists... try and use that. + # Not likely to be there on pre 10.4.x ... or MacOS 10.10+ + if exists('/usr/X11/bin/fc-list'): + fonts = initsysfonts_unix('/usr/X11/bin/fc-list') + # This fc-list path will work with the X11 from the OS X 10.3 installation + # disc + elif exists('/usr/X11R6/bin/fc-list'): + fonts = initsysfonts_unix('/usr/X11R6/bin/fc-list') + elif exists('/usr/sbin/system_profiler'): + try: + fonts = _system_profiler_darwin() + except (OSError, ValueError): + fonts = {} + else: + fonts = {} + + return fonts + + +# read the fonts on unix +def initsysfonts_unix(path="fc-list"): + """use the fc-list from fontconfig to get a list of fonts""" + fonts = {} + + try: + # note, we capture stderr so if fc-list isn't there to stop stderr + # printing. + flout, flerr = subprocess.Popen('%s : file family style' % path, shell=True, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + close_fds=True).communicate() + except Exception: + return fonts + + entries = toascii(flout) + try: + for line in entries.split('\n'): + + try: + filename, family, style = line.split(':', 2) + if splitext(filename)[1].lower() in OpenType_extensions: + bold = 'Bold' in style + italic = 'Italic' in style + oblique = 'Oblique' in style + for name in family.strip().split(','): + if name: + break + else: + name = splitext(basename(filename))[0] + + _addfont( + _simplename(name), bold, italic or oblique, filename, fonts) + + except Exception: + # try the next one. + pass + + except Exception: + pass + + return fonts + + +def create_aliases(): + """map common fonts that are absent from the system to similar fonts that are installed in the system""" + alias_groups = ( + ('monospace', 'misc-fixed', 'courier', 'couriernew', 'console', + 'fixed', 'mono', 'freemono', 'bitstreamverasansmono', + 'verasansmono', 'monotype', 'lucidaconsole'), + ('sans', 'arial', 'helvetica', 'swiss', 'freesans', + 'bitstreamverasans', 'verasans', 'verdana', 'tahoma'), + ('serif', 'times', 'freeserif', 'bitstreamveraserif', 'roman', + 'timesroman', 'timesnewroman', 'dutch', 'veraserif', + 'georgia'), + ('wingdings', 'wingbats'), + ) + for alias_set in alias_groups: + for name in alias_set: + if name in Sysfonts: + found = Sysfonts[name] + break + else: + continue + for name in alias_set: + if name not in Sysfonts: + Sysalias[name] = found + + +# initialize it all, called once +def initsysfonts(): + if sys.platform == 'win32': + fonts = initsysfonts_win32() + elif sys.platform == 'darwin': + fonts = initsysfonts_darwin() + else: + fonts = initsysfonts_unix() + Sysfonts.update(fonts) + create_aliases() + if not Sysfonts: # dummy so we don't try to reinit + Sysfonts[None] = None + + +# pygame.font specific declarations +def font_constructor(fontpath, size, bold, italic): + import pygame.font + + font = pygame.font.Font(fontpath, size) + if bold: + font.set_bold(1) + if italic: + font.set_italic(1) + + return font + + +# the exported functions + +def SysFont(name, size, bold=False, italic=False, constructor=None): + """pygame.font.SysFont(name, size, bold=False, italic=False, constructor=None) -> Font + create a pygame Font from system font resources + + This will search the system fonts for the given font + name. You can also enable bold or italic styles, and + the appropriate system font will be selected if available. + + This will always return a valid Font object, and will + fallback on the builtin pygame font if the given font + is not found. + + Name can also be a comma separated list of names, in + which case set of names will be searched in order. Pygame + uses a small set of common font aliases, if the specific + font you ask for is not available, a reasonable alternative + may be used. + + if optional constructor is provided, it must be a function with + signature constructor(fontpath, size, bold, italic) which returns + a Font instance. If None, a pygame.font.Font object is created. + """ + if constructor is None: + constructor = font_constructor + + if not Sysfonts: + initsysfonts() + + gotbold = gotitalic = False + fontname = None + if name: + allnames = name + for name in allnames.split(','): + name = _simplename(name) + styles = Sysfonts.get(name) + if not styles: + styles = Sysalias.get(name) + if styles: + plainname = styles.get((False, False)) + fontname = styles.get((bold, italic)) + if not fontname and not plainname: + # Neither requested style, nor plain font exists, so + # return a font with the name requested, but an + # arbitrary style. + (style, fontname) = list(styles.items())[0] + # Attempt to style it as requested. This can't + # unbold or unitalicize anything, but it can + # fake bold and/or fake italicize. + if bold and style[0]: + gotbold = True + if italic and style[1]: + gotitalic = True + elif not fontname: + fontname = plainname + elif plainname != fontname: + gotbold = bold + gotitalic = italic + if fontname: + break + + set_bold = set_italic = False + if bold and not gotbold: + set_bold = True + if italic and not gotitalic: + set_italic = True + + return constructor(fontname, size, set_bold, set_italic) + + +def get_fonts(): + """pygame.font.get_fonts() -> list + get a list of system font names + + Returns the list of all found system fonts. Note that + the names of the fonts will be all lowercase with spaces + removed. This is how pygame internally stores the font + names for matching. + """ + if not Sysfonts: + initsysfonts() + return list(Sysfonts) + + +def match_font(name, bold=0, italic=0): + """pygame.font.match_font(name, bold=0, italic=0) -> name + find the filename for the named system font + + This performs the same font search as the SysFont() + function, only it returns the path to the TTF file + that would be loaded. The font name can be a comma + separated list of font names to try. + + If no match is found, None is returned. + """ + if not Sysfonts: + initsysfonts() + + fontname = None + allnames = name + for name in allnames.split(','): + name = _simplename(name) + styles = Sysfonts.get(name) + if not styles: + styles = Sysalias.get(name) + if styles: + while not fontname: + fontname = styles.get((bold, italic)) + if italic: + italic = 0 + elif bold: + bold = 0 + elif not fontname: + fontname = list(styles.values())[0] + if fontname: + break + return fontname diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/__init__.py new file mode 100644 index 0000000..dd26586 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/__init__.py @@ -0,0 +1,40 @@ +"""Pygame unit test suite package + +Exports function run() + +A quick way to run the test suite package from the command line +is by importing the go submodule: + +python -m "import pygame.tests" [] + +Command line option --help displays a usage message. Available options +correspond to the pygame.tests.run arguments. + +The xxxx_test submodules of the tests package are unit test suites for +individual parts of Pygame. Each can also be run as a main program. This is +useful if the test, such as cdrom_test, is interactive. + +For Pygame development the test suite can be run from a Pygame distribution +root directory using run_tests.py. Alternately, test/__main__.py can be run +directly. + +""" + +if __name__ == "pygame.tests": + from pygame.tests.test_utils.run_tests import run +elif __name__ == "__main__": + import os + import sys + + pkg_dir = os.path.split(os.path.abspath(__file__))[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) + + if is_pygame_pkg: + import pygame.tests.__main__ + else: + import test.__main__ +else: + from test.test_utils.run_tests import run diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/__main__.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/__main__.py new file mode 100644 index 0000000..abdd92e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/__main__.py @@ -0,0 +1,144 @@ +"""Load and run the Pygame test suite + +python -c "import pygame.tests.go" [] + +or + +python test/go.py [] + +Command line option --help displays a command line usage message. + +run_tests.py in the main distribution directory is an alternative to test.go + +""" + +import sys + +if __name__ == "__main__": + import os + + pkg_dir = os.path.split(os.path.abspath(__file__))[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +if is_pygame_pkg: + from pygame.tests.test_utils.run_tests import run_and_exit + from pygame.tests.test_utils.test_runner import opt_parser +else: + from test.test_utils.run_tests import run_and_exit + from test.test_utils.test_runner import opt_parser + +if is_pygame_pkg: + test_pkg_name = "pygame.tests" +else: + test_pkg_name = "test" +program_name = sys.argv[0] +if program_name == "-c": + program_name = 'python -c "import %s.go"' % test_pkg_name + +########################################################################### +# Set additional command line options +# +# Defined in test_runner.py as it shares options, added to here + +opt_parser.set_usage( + """ + +Runs all or some of the %(pkg)s.xxxx_test tests. + +$ %(exec)s sprite threads -sd + +Runs the sprite and threads module tests isolated in subprocesses, dumping +all failing tests info in the form of a dict. + +""" + % {"pkg": test_pkg_name, "exec": program_name} +) + +opt_parser.add_option( + "-d", "--dump", action="store_true", help="dump results as dict ready to eval" +) + +opt_parser.add_option("-F", "--file", help="dump results to a file") + +opt_parser.add_option( + "-m", + "--multi_thread", + metavar="THREADS", + type="int", + help="run subprocessed tests in x THREADS", +) + +opt_parser.add_option( + "-t", + "--time_out", + metavar="SECONDS", + type="int", + help="kill stalled subprocessed tests after SECONDS", +) + +opt_parser.add_option( + "-f", "--fake", metavar="DIR", help="run fake tests in run_tests__tests/$DIR" +) + +opt_parser.add_option( + "-p", + "--python", + metavar="PYTHON", + help="path to python excutable to run subproccesed tests\n" + "default (sys.executable): %s" % sys.executable, +) + +opt_parser.add_option( + "-I", + "--interactive", + action="store_true", + help="include tests requiring user input", +) + +opt_parser.add_option("-S", "--seed", type="int", help="Randomisation seed") + +########################################################################### +# Set run() keyword arguements according to command line arguemnts. +# args will be the test module list, passed as positional argumemts. + +options, args = opt_parser.parse_args() +kwds = {} +if options.incomplete: + kwds["incomplete"] = True +if options.usesubprocess: + kwds["usesubprocess"] = True +else: + kwds["usesubprocess"] = False +if options.dump: + kwds["dump"] = True +if options.file: + kwds["file"] = options.file +if options.exclude: + kwds["exclude"] = options.exclude +if options.unbuffered: + kwds["unbuffered"] = True +if options.randomize: + kwds["randomize"] = True +if options.seed is not None: + kwds["seed"] = options.seed +if options.multi_thread is not None: + kwds["multi_thread"] = options.multi_thread +if options.time_out is not None: + kwds["time_out"] = options.time_out +if options.fake: + kwds["fake"] = options.fake +if options.python: + kwds["python"] = options.python +if options.interactive: + kwds["interactive"] = True +kwds["verbosity"] = options.verbosity if options.verbosity is not None else 1 + + +########################################################################### +# Run the test suite. +run_and_exit(*args, **kwds) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/base_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/base_test.py new file mode 100644 index 0000000..dc47bef --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/base_test.py @@ -0,0 +1,664 @@ +# -*- coding: utf8 -*- + +import sys +import unittest + +import platform + +IS_PYPY = "PyPy" == platform.python_implementation() + +try: + from pygame.tests.test_utils import arrinter +except NameError: + pass +import pygame + + +init_called = quit_called = 0 + + +def __PYGAMEinit__(): # called automatically by pygame.init() + global init_called + init_called = init_called + 1 + pygame.register_quit(pygame_quit) + + # Returning False indicates that the initialization has failed. It is + # purposely done here to test that failing modules are reported. + return False + + +def pygame_quit(): + global quit_called + quit_called = quit_called + 1 + + +quit_hook_ran = 0 + + +def quit_hook(): + global quit_hook_ran + quit_hook_ran = 1 + + +class BaseModuleTest(unittest.TestCase): + def tearDown(self): + # Clean up after each test method. + pygame.quit() + + def testAutoInit(self): + pygame.init() + pygame.quit() + self.assertEqual(init_called, 1) + self.assertEqual(quit_called, 1) + + def test_get_sdl_byteorder(self): + """Ensure the SDL byte order is valid""" + byte_order = pygame.get_sdl_byteorder() + expected_options = (pygame.LIL_ENDIAN, pygame.BIG_ENDIAN) + + self.assertIn(byte_order, expected_options) + + def test_get_sdl_version(self): + """Ensure the SDL version is valid""" + self.assertEqual(len(pygame.get_sdl_version()), 3) + + class ExporterBase(object): + def __init__(self, shape, typechar, itemsize): + import ctypes + + ndim = len(shape) + self.ndim = ndim + self.shape = tuple(shape) + array_len = 1 + for d in shape: + array_len *= d + self.size = itemsize * array_len + self.parent = ctypes.create_string_buffer(self.size) + self.itemsize = itemsize + strides = [itemsize] * ndim + for i in range(ndim - 1, 0, -1): + strides[i - 1] = strides[i] * shape[i] + self.strides = tuple(strides) + self.data = ctypes.addressof(self.parent), False + if self.itemsize == 1: + byteorder = "|" + elif sys.byteorder == "big": + byteorder = ">" + else: + byteorder = "<" + self.typestr = byteorder + typechar + str(self.itemsize) + + def assertSame(self, proxy, obj): + self.assertEqual(proxy.length, obj.size) + iface = proxy.__array_interface__ + self.assertEqual(iface["typestr"], obj.typestr) + self.assertEqual(iface["shape"], obj.shape) + self.assertEqual(iface["strides"], obj.strides) + self.assertEqual(iface["data"], obj.data) + + def test_PgObject_GetBuffer_array_interface(self): + from pygame.bufferproxy import BufferProxy + + class Exporter(self.ExporterBase): + def get__array_interface__(self): + return { + "version": 3, + "typestr": self.typestr, + "shape": self.shape, + "strides": self.strides, + "data": self.data, + } + + __array_interface__ = property(get__array_interface__) + # Should be ignored by PgObject_GetBuffer + __array_struct__ = property(lambda self: None) + + _shape = [2, 3, 5, 7, 11] # Some prime numbers + for ndim in range(1, len(_shape)): + o = Exporter(_shape[0:ndim], "i", 2) + v = BufferProxy(o) + self.assertSame(v, o) + ndim = 2 + shape = _shape[0:ndim] + for typechar in ("i", "u"): + for itemsize in (1, 2, 4, 8): + o = Exporter(shape, typechar, itemsize) + v = BufferProxy(o) + self.assertSame(v, o) + for itemsize in (4, 8): + o = Exporter(shape, "f", itemsize) + v = BufferProxy(o) + self.assertSame(v, o) + + # Is the dict received from an exporting object properly released? + # The dict should be freed before PgObject_GetBuffer returns. + # When the BufferProxy v's length property is referenced, v calls + # PgObject_GetBuffer, which in turn references Exporter2 o's + # __array_interface__ property. The Exporter2 instance o returns a + # dict subclass for which it keeps both a regular reference and a + # weak reference. The regular reference should be the only + # remaining reference when PgObject_GetBuffer returns. This is + # verified by first checking the weak reference both before and + # after the regular reference held by o is removed. + + import weakref, gc + + class NoDictError(RuntimeError): + pass + + class WRDict(dict): + """Weak referenceable dict""" + + pass + + class Exporter2(Exporter): + def get__array_interface__2(self): + self.d = WRDict(Exporter.get__array_interface__(self)) + self.dict_ref = weakref.ref(self.d) + return self.d + + __array_interface__ = property(get__array_interface__2) + + def free_dict(self): + self.d = None + + def is_dict_alive(self): + try: + return self.dict_ref() is not None + except AttributeError: + raise NoDictError("__array_interface__ is unread") + + o = Exporter2((2, 4), "u", 4) + v = BufferProxy(o) + self.assertRaises(NoDictError, o.is_dict_alive) + length = v.length + self.assertTrue(o.is_dict_alive()) + o.free_dict() + gc.collect() + self.assertFalse(o.is_dict_alive()) + + def test_GetView_array_struct(self): + from pygame.bufferproxy import BufferProxy + + class Exporter(self.ExporterBase): + def __init__(self, shape, typechar, itemsize): + super(Exporter, self).__init__(shape, typechar, itemsize) + self.view = BufferProxy(self.__dict__) + + def get__array_struct__(self): + return self.view.__array_struct__ + + __array_struct__ = property(get__array_struct__) + # Should not cause PgObject_GetBuffer to fail + __array_interface__ = property(lambda self: None) + + _shape = [2, 3, 5, 7, 11] # Some prime numbers + for ndim in range(1, len(_shape)): + o = Exporter(_shape[0:ndim], "i", 2) + v = BufferProxy(o) + self.assertSame(v, o) + ndim = 2 + shape = _shape[0:ndim] + for typechar in ("i", "u"): + for itemsize in (1, 2, 4, 8): + o = Exporter(shape, typechar, itemsize) + v = BufferProxy(o) + self.assertSame(v, o) + for itemsize in (4, 8): + o = Exporter(shape, "f", itemsize) + v = BufferProxy(o) + self.assertSame(v, o) + + # Check returned cobject/capsule reference count + try: + from sys import getrefcount + except ImportError: + # PyPy: no reference counting + pass + else: + o = Exporter(shape, typechar, itemsize) + self.assertEqual(getrefcount(o.__array_struct__), 1) + + if pygame.HAVE_NEWBUF: + from pygame.tests.test_utils import buftools + + def NEWBUF_assertSame(self, proxy, exp): + buftools = self.buftools + Importer = buftools.Importer + self.assertEqual(proxy.length, exp.len) + imp = Importer(proxy, buftools.PyBUF_RECORDS_RO) + self.assertEqual(imp.readonly, exp.readonly) + self.assertEqual(imp.format, exp.format) + self.assertEqual(imp.itemsize, exp.itemsize) + self.assertEqual(imp.ndim, exp.ndim) + self.assertEqual(imp.shape, exp.shape) + self.assertEqual(imp.strides, exp.strides) + self.assertTrue(imp.suboffsets is None) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_newbuf(self): + from pygame.bufferproxy import BufferProxy + + Exporter = self.buftools.Exporter + _shape = [2, 3, 5, 7, 11] # Some prime numbers + for ndim in range(1, len(_shape)): + o = Exporter(_shape[0:ndim], "=h") + v = BufferProxy(o) + self.NEWBUF_assertSame(v, o) + ndim = 2 + shape = _shape[0:ndim] + for format in [ + "b", + "B", + "=h", + "=H", + "=i", + "=I", + "=q", + "=Q", + "f", + "d", + "1h", + "=1h", + "x", + "1x", + "2x", + "3x", + "4x", + "5x", + "6x", + "7x", + "8x", + "9x", + ]: + o = Exporter(shape, format) + v = BufferProxy(o) + self.NEWBUF_assertSame(v, o) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_bad_format(self): + from pygame.bufferproxy import BufferProxy + from pygame.newbuffer import BufferMixin + from ctypes import create_string_buffer, addressof + + buftools = self.buftools + Exporter = buftools.Exporter + Importer = buftools.Importer + PyBUF_FORMAT = buftools.PyBUF_FORMAT + + for format in [ + "", + "=", + "1", + " ", + "2h", + "=2h", + "0x", + "11x", + "=!", + "h ", + " h", + "hh", + "?", + ]: + exp = Exporter((1,), format, itemsize=2) + b = BufferProxy(exp) + self.assertRaises(ValueError, Importer, b, PyBUF_FORMAT) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_PgDict_AsBuffer_PyBUF_flags(self): + from pygame.bufferproxy import BufferProxy + + is_lil_endian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN + fsys, frev = ("<", ">") if is_lil_endian else (">", "<") + buftools = self.buftools + Importer = buftools.Importer + a = BufferProxy( + {"typestr": "|u4", "shape": (10, 2), "data": (9, False)} + ) # 9? No data accesses. + b = Importer(a, buftools.PyBUF_SIMPLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 4) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, 9) + b = Importer(a, buftools.PyBUF_WRITABLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 4) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, 9) + b = Importer(a, buftools.PyBUF_ND) + self.assertEqual(b.ndim, 2) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 4) + self.assertEqual(b.shape, (10, 2)) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, 9) + a = BufferProxy( + { + "typestr": fsys + "i2", + "shape": (5, 10), + "strides": (24, 2), + "data": (42, False), + } + ) # 42? No data accesses. + b = Importer(a, buftools.PyBUF_STRIDES) + self.assertEqual(b.ndim, 2) + self.assertTrue(b.format is None) + self.assertEqual(b.len, 100) + self.assertEqual(b.itemsize, 2) + self.assertEqual(b.shape, (5, 10)) + self.assertEqual(b.strides, (24, 2)) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, 42) + b = Importer(a, buftools.PyBUF_FULL_RO) + self.assertEqual(b.ndim, 2) + self.assertEqual(b.format, "=h") + self.assertEqual(b.len, 100) + self.assertEqual(b.itemsize, 2) + self.assertEqual(b.shape, (5, 10)) + self.assertEqual(b.strides, (24, 2)) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, 42) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_CONTIG) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_CONTIG) + a = BufferProxy( + { + "typestr": frev + "i2", + "shape": (3, 5, 10), + "strides": (120, 24, 2), + "data": (1000000, True), + } + ) # 1000000? No data accesses. + b = Importer(a, buftools.PyBUF_FULL_RO) + self.assertEqual(b.ndim, 3) + self.assertEqual(b.format, frev + "h") + self.assertEqual(b.len, 300) + self.assertEqual(b.itemsize, 2) + self.assertEqual(b.shape, (3, 5, 10)) + self.assertEqual(b.strides, (120, 24, 2)) + self.assertTrue(b.suboffsets is None) + self.assertTrue(b.readonly) + self.assertEqual(b.buf, 1000000) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FULL) + + @unittest.skipIf(IS_PYPY or (not pygame.HAVE_NEWBUF), "newbuf with ctypes") + def test_PgObject_AsBuffer_PyBUF_flags(self): + from pygame.bufferproxy import BufferProxy + import ctypes + + is_lil_endian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN + fsys, frev = ("<", ">") if is_lil_endian else (">", "<") + buftools = self.buftools + Importer = buftools.Importer + e = arrinter.Exporter( + (10, 2), typekind="f", itemsize=ctypes.sizeof(ctypes.c_double) + ) + a = BufferProxy(e) + b = Importer(a, buftools.PyBUF_SIMPLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, e.len) + self.assertEqual(b.itemsize, e.itemsize) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, e.data) + b = Importer(a, buftools.PyBUF_WRITABLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, e.len) + self.assertEqual(b.itemsize, e.itemsize) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, e.data) + b = Importer(a, buftools.PyBUF_ND) + self.assertEqual(b.ndim, e.nd) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, e.itemsize) + self.assertEqual(b.shape, e.shape) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, e.data) + e = arrinter.Exporter((5, 10), typekind="i", itemsize=2, strides=(24, 2)) + a = BufferProxy(e) + b = Importer(a, buftools.PyBUF_STRIDES) + self.assertEqual(b.ndim, e.nd) + self.assertTrue(b.format is None) + self.assertEqual(b.len, e.len) + self.assertEqual(b.itemsize, e.itemsize) + self.assertEqual(b.shape, e.shape) + self.assertEqual(b.strides, e.strides) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, e.data) + b = Importer(a, buftools.PyBUF_FULL_RO) + self.assertEqual(b.ndim, e.nd) + self.assertEqual(b.format, "=h") + self.assertEqual(b.len, e.len) + self.assertEqual(b.itemsize, e.itemsize) + self.assertEqual(b.shape, e.shape) + self.assertEqual(b.strides, e.strides) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, e.data) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_CONTIG) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_CONTIG) + e = arrinter.Exporter( + (3, 5, 10), + typekind="i", + itemsize=2, + strides=(120, 24, 2), + flags=arrinter.PAI_ALIGNED, + ) + a = BufferProxy(e) + b = Importer(a, buftools.PyBUF_FULL_RO) + self.assertEqual(b.ndim, e.nd) + self.assertEqual(b.format, frev + "h") + self.assertEqual(b.len, e.len) + self.assertEqual(b.itemsize, e.itemsize) + self.assertEqual(b.shape, e.shape) + self.assertEqual(b.strides, e.strides) + self.assertTrue(b.suboffsets is None) + self.assertTrue(b.readonly) + self.assertEqual(b.buf, e.data) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FULL) + + def test_PgObject_GetBuffer_exception(self): + # For consistency with surfarray + from pygame.bufferproxy import BufferProxy + + bp = BufferProxy(1) + self.assertRaises(ValueError, getattr, bp, "length") + + def not_init_assertions(self): + self.assertFalse(pygame.get_init(), "pygame shouldn't be initialized") + self.assertFalse(pygame.display.get_init(), "display shouldn't be initialized") + + if "pygame.mixer" in sys.modules: + self.assertFalse(pygame.mixer.get_init(), "mixer shouldn't be initialized") + + if "pygame.font" in sys.modules: + self.assertFalse(pygame.font.get_init(), "init shouldn't be initialized") + + ## !!! TODO : Remove when scrap works for OS X + import platform + + if platform.system().startswith("Darwin"): + return + + try: + self.assertRaises(pygame.error, pygame.scrap.get) + except NotImplementedError: + # Scrap is optional. + pass + + # pygame.cdrom + # pygame.joystick + + def init_assertions(self): + self.assertTrue(pygame.get_init()) + self.assertTrue(pygame.display.get_init()) + + if "pygame.mixer" in sys.modules: + self.assertTrue(pygame.mixer.get_init()) + + if "pygame.font" in sys.modules: + self.assertTrue(pygame.font.get_init()) + + def test_quit__and_init(self): + # __doc__ (as of 2008-06-25) for pygame.base.quit: + + # pygame.quit(): return None + # uninitialize all pygame modules + + # Make sure everything is not init + self.not_init_assertions() + + # Initiate it + pygame.init() + + # Check + self.init_assertions() + + # Quit + pygame.quit() + + # All modules have quit + self.not_init_assertions() + + def test_register_quit(self): + """Ensure that a registered function is called on quit()""" + self.assertFalse(quit_hook_ran) + + pygame.init() + pygame.register_quit(quit_hook) + pygame.quit() + + self.assertTrue(quit_hook_ran) + + def test_get_error(self): + + # __doc__ (as of 2008-08-02) for pygame.base.get_error: + + # pygame.get_error(): return errorstr + # get the current error message + # + # SDL maintains an internal error message. This message will usually + # be given to you when pygame.error is raised. You will rarely need to + # call this function. + # + + # The first error could be all sorts of nonsense or empty. + e = pygame.get_error() + pygame.set_error("hi") + self.assertEqual(pygame.get_error(), "hi") + pygame.set_error("") + self.assertEqual(pygame.get_error(), "") + + def test_set_error(self): + + # The first error could be all sorts of nonsense or empty. + e = pygame.get_error() + pygame.set_error("hi") + self.assertEqual(pygame.get_error(), "hi") + pygame.set_error("") + self.assertEqual(pygame.get_error(), "") + + def test_unicode_error(self): + if sys.version_info.major > 2: + pygame.set_error(u"你好") + self.assertEqual(u"你好", pygame.get_error()) + else: + # no unicode objects for now + pygame.set_error(u"你好") + encstr = u"你好".encode("utf8") + self.assertEqual(encstr, pygame.get_error()) + + def test_init(self): + """Ensures init() works properly.""" + # Make sure nothing initialized. + self.not_init_assertions() + + # The exact number of modules can change, but it should never be < 0. + expected_min_passes = 0 + + # The __PYGAMEinit__ function in this module returns False, so this + # should give a fail count of 1. All other modules should pass. + expected_fails = 1 + + passes, fails = pygame.init() + + self.init_assertions() + self.assertGreaterEqual(passes, expected_min_passes) + self.assertEqual(fails, expected_fails) + + def test_get_init(self): + # Test if get_init() gets the init state. + self.assertFalse(pygame.get_init()) + + def test_get_init__after_init(self): + # Test if get_init() gets the init state after pygame.init() called. + pygame.init() + + self.assertTrue(pygame.get_init()) + + def test_get_init__after_quit(self): + # Test if get_init() gets the init state after pygame.quit() called. + pygame.init() + pygame.quit() + + self.assertFalse(pygame.get_init()) + + def todo_test_segfault(self): + + # __doc__ (as of 2008-08-02) for pygame.base.segfault: + + # crash + + self.fail() + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/blit_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/blit_test.py new file mode 100644 index 0000000..9e8f8fc --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/blit_test.py @@ -0,0 +1,157 @@ +import unittest + +import pygame +from pygame.locals import * + + +class BlitTest(unittest.TestCase): + def test_SRCALPHA(self): + """ SRCALPHA tests. + """ + # blend(s, 0, d) = d + s = pygame.Surface((1, 1), SRCALPHA, 32) + s.fill((255, 255, 255, 0)) + + d = pygame.Surface((1, 1), SRCALPHA, 32) + d.fill((0, 0, 255, 255)) + + s.blit(d, (0, 0)) + self.assertEqual(s.get_at((0, 0)), d.get_at((0, 0))) + + # blend(s, 255, d) = s + s = pygame.Surface((1, 1), SRCALPHA, 32) + s.fill((123, 0, 0, 255)) + s1 = pygame.Surface((1, 1), SRCALPHA, 32) + s1.fill((123, 0, 0, 255)) + d = pygame.Surface((1, 1), SRCALPHA, 32) + d.fill((10, 0, 0, 0)) + s.blit(d, (0, 0)) + self.assertEqual(s.get_at((0, 0)), s1.get_at((0, 0))) + + # TODO: these should be true too. + # blend(0, sA, 0) = 0 + # blend(255, sA, 255) = 255 + # blend(s, sA, d) <= 255 + + def test_BLEND(self): + """ BLEND_ tests. + """ + + # test that it doesn't overflow, and that it is saturated. + s = pygame.Surface((1, 1), SRCALPHA, 32) + s.fill((255, 255, 255, 0)) + + d = pygame.Surface((1, 1), SRCALPHA, 32) + d.fill((0, 0, 255, 255)) + + s.blit(d, (0, 0), None, BLEND_ADD) + + # print "d %s" % (d.get_at((0,0)),) + # print s.get_at((0,0)) + # self.assertEqual(s.get_at((0,0))[2], 255 ) + # self.assertEqual(s.get_at((0,0))[3], 0 ) + + s.blit(d, (0, 0), None, BLEND_RGBA_ADD) + # print s.get_at((0,0)) + self.assertEqual(s.get_at((0, 0))[3], 255) + + # test adding works. + s.fill((20, 255, 255, 0)) + d.fill((10, 0, 255, 255)) + s.blit(d, (0, 0), None, BLEND_ADD) + self.assertEqual(s.get_at((0, 0))[2], 255) + + # test subbing works. + s.fill((20, 255, 255, 0)) + d.fill((10, 0, 255, 255)) + s.blit(d, (0, 0), None, BLEND_SUB) + self.assertEqual(s.get_at((0, 0))[0], 10) + + # no overflow in sub blend. + s.fill((20, 255, 255, 0)) + d.fill((30, 0, 255, 255)) + s.blit(d, (0, 0), None, BLEND_SUB) + self.assertEqual(s.get_at((0, 0))[0], 0) + + def make_blit_list(self, num_surfs): + + blit_list = [] + for i in range(num_surfs): + dest = (i * 10, 0) + surf = pygame.Surface((10, 10), SRCALPHA, 32) + color = (i * 1, i * 1, i * 1) + surf.fill(color) + blit_list.append((surf, dest)) + return blit_list + + def test_blits(self): + + NUM_SURFS = 255 + PRINT_TIMING = 0 + dst = pygame.Surface((NUM_SURFS * 10, 10), SRCALPHA, 32) + dst.fill((230, 230, 230)) + blit_list = self.make_blit_list(NUM_SURFS) + + def blits(blit_list): + for surface, dest in blit_list: + dst.blit(surface, dest) + + from time import time + + t0 = time() + results = blits(blit_list) + t1 = time() + if PRINT_TIMING: + print("python blits: %s" % (t1 - t0)) + + dst.fill((230, 230, 230)) + t0 = time() + results = dst.blits(blit_list) + t1 = time() + if PRINT_TIMING: + print("Surface.blits :%s" % (t1 - t0)) + + # check if we blit all the different colors in the correct spots. + for i in range(NUM_SURFS): + color = (i * 1, i * 1, i * 1) + self.assertEqual(dst.get_at((i * 10, 0)), color) + self.assertEqual(dst.get_at(((i * 10) + 5, 5)), color) + + self.assertEqual(len(results), NUM_SURFS) + + t0 = time() + results = dst.blits(blit_list, doreturn=0) + t1 = time() + if PRINT_TIMING: + print("Surface.blits doreturn=0: %s" % (t1 - t0)) + self.assertEqual(results, None) + + t0 = time() + results = dst.blits(((surf, dest) for surf, dest in blit_list)) + t1 = time() + if PRINT_TIMING: + print("Surface.blits generator: %s" % (t1 - t0)) + + def test_blits_not_sequence(self): + dst = pygame.Surface((100, 10), SRCALPHA, 32) + self.assertRaises(ValueError, dst.blits, None) + + def test_blits_wrong_length(self): + dst = pygame.Surface((100, 10), SRCALPHA, 32) + self.assertRaises( + ValueError, dst.blits, [pygame.Surface((10, 10), SRCALPHA, 32)] + ) + + def test_blits_bad_surf_args(self): + dst = pygame.Surface((100, 10), SRCALPHA, 32) + self.assertRaises(TypeError, dst.blits, [(None, None)]) + + def test_blits_bad_dest(self): + dst = pygame.Surface((100, 10), SRCALPHA, 32) + self.assertRaises( + TypeError, dst.blits, [(pygame.Surface((10, 10), SRCALPHA, 32), None)] + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/bufferproxy_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/bufferproxy_test.py new file mode 100644 index 0000000..4edbc5c --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/bufferproxy_test.py @@ -0,0 +1,509 @@ +import sys +import re +import weakref +import gc +import ctypes +import unittest + + +import pygame +from pygame.bufferproxy import BufferProxy +from pygame.compat import as_bytes + +try: + BufferError +except NameError: + from pygame import BufferError + + +class BufferProxyTest(unittest.TestCase): + view_keywords = { + "shape": (5, 4, 3), + "typestr": "|u1", + "data": (0, True), + "strides": (4, 20, 1), + } + + def test_module_name(self): + self.assertEqual(pygame.bufferproxy.__name__, "pygame.bufferproxy") + + def test_class_name(self): + self.assertEqual(BufferProxy.__name__, "BufferProxy") + + def test___array_struct___property(self): + kwds = self.view_keywords + v = BufferProxy(kwds) + d = pygame.get_array_interface(v) + self.assertEqual(len(d), 5) + self.assertEqual(d["version"], 3) + self.assertEqual(d["shape"], kwds["shape"]) + self.assertEqual(d["typestr"], kwds["typestr"]) + self.assertEqual(d["data"], kwds["data"]) + self.assertEqual(d["strides"], kwds["strides"]) + + def test___array_interface___property(self): + kwds = self.view_keywords + v = BufferProxy(kwds) + d = v.__array_interface__ + self.assertEqual(len(d), 5) + self.assertEqual(d["version"], 3) + self.assertEqual(d["shape"], kwds["shape"]) + self.assertEqual(d["typestr"], kwds["typestr"]) + self.assertEqual(d["data"], kwds["data"]) + self.assertEqual(d["strides"], kwds["strides"]) + + def test_parent_property(self): + kwds = dict(self.view_keywords) + p = [] + kwds["parent"] = p + v = BufferProxy(kwds) + + self.assertIs(v.parent, p) + + def test_before(self): + def callback(parent): + success.append(parent is p) + + class MyException(Exception): + pass + + def raise_exception(parent): + raise MyException("Just a test.") + + kwds = dict(self.view_keywords) + p = [] + kwds["parent"] = p + + # For array interface + success = [] + kwds["before"] = callback + v = BufferProxy(kwds) + self.assertEqual(len(success), 0) + d = v.__array_interface__ + self.assertEqual(len(success), 1) + self.assertTrue(success[0]) + d = v.__array_interface__ + self.assertEqual(len(success), 1) + d = v = None + gc.collect() + self.assertEqual(len(success), 1) + + # For array struct + success = [] + kwds["before"] = callback + v = BufferProxy(kwds) + self.assertEqual(len(success), 0) + c = v.__array_struct__ + self.assertEqual(len(success), 1) + self.assertTrue(success[0]) + c = v.__array_struct__ + self.assertEqual(len(success), 1) + c = v = None + gc.collect() + self.assertEqual(len(success), 1) + + # Callback raises an exception + kwds["before"] = raise_exception + v = BufferProxy(kwds) + self.assertRaises(MyException, lambda: v.__array_struct__) + + def test_after(self): + def callback(parent): + success.append(parent is p) + + kwds = dict(self.view_keywords) + p = [] + kwds["parent"] = p + + # For array interface + success = [] + kwds["after"] = callback + v = BufferProxy(kwds) + self.assertEqual(len(success), 0) + d = v.__array_interface__ + self.assertEqual(len(success), 0) + d = v.__array_interface__ + self.assertEqual(len(success), 0) + d = v = None + gc.collect() + self.assertEqual(len(success), 1) + self.assertTrue(success[0]) + + # For array struct + success = [] + kwds["after"] = callback + v = BufferProxy(kwds) + self.assertEqual(len(success), 0) + c = v.__array_struct__ + self.assertEqual(len(success), 0) + c = v.__array_struct__ + self.assertEqual(len(success), 0) + c = v = None + gc.collect() + self.assertEqual(len(success), 1) + self.assertTrue(success[0]) + + def test_attribute(self): + v = BufferProxy(self.view_keywords) + self.assertRaises(AttributeError, getattr, v, "undefined") + v.undefined = 12 + self.assertEqual(v.undefined, 12) + del v.undefined + self.assertRaises(AttributeError, getattr, v, "undefined") + + def test_weakref(self): + v = BufferProxy(self.view_keywords) + weak_v = weakref.ref(v) + + self.assertIs(weak_v(), v) + + v = None + gc.collect() + + self.assertIsNone(weak_v()) + + def test_gc(self): + """refcount agnostic check that contained objects are freed""" + + def before_callback(parent): + return r[0] + + def after_callback(parent): + return r[1] + + class Obj(object): + pass + + p = Obj() + a = Obj() + r = [Obj(), Obj()] + weak_p = weakref.ref(p) + weak_a = weakref.ref(a) + weak_r0 = weakref.ref(r[0]) + weak_r1 = weakref.ref(r[1]) + weak_before = weakref.ref(before_callback) + weak_after = weakref.ref(after_callback) + kwds = dict(self.view_keywords) + kwds["parent"] = p + kwds["before"] = before_callback + kwds["after"] = after_callback + v = BufferProxy(kwds) + v.some_attribute = a + weak_v = weakref.ref(v) + kwds = p = a = before_callback = after_callback = None + gc.collect() + self.assertTrue(weak_p() is not None) + self.assertTrue(weak_a() is not None) + self.assertTrue(weak_before() is not None) + self.assertTrue(weak_after() is not None) + v = None + [gc.collect() for x in range(4)] + self.assertTrue(weak_v() is None) + self.assertTrue(weak_p() is None) + self.assertTrue(weak_a() is None) + self.assertTrue(weak_before() is None) + self.assertTrue(weak_after() is None) + self.assertTrue(weak_r0() is not None) + self.assertTrue(weak_r1() is not None) + r = None + gc.collect() + self.assertTrue(weak_r0() is None) + self.assertTrue(weak_r1() is None) + + # Cycle removal + kwds = dict(self.view_keywords) + kwds["parent"] = [] + v = BufferProxy(kwds) + v.some_attribute = v + tracked = True + for o in gc.get_objects(): + if o is v: + break + else: + tracked = False + self.assertTrue(tracked) + kwds["parent"].append(v) + kwds = None + gc.collect() + n1 = len(gc.garbage) + v = None + gc.collect() + n2 = len(gc.garbage) + self.assertEqual(n2, n1) + + def test_c_api(self): + api = pygame.bufferproxy._PYGAME_C_API + api_type = type(pygame.base._PYGAME_C_API) + + self.assertIsInstance(api, api_type) + + def test_repr(self): + v = BufferProxy(self.view_keywords) + cname = BufferProxy.__name__ + oname, ovalue = re.findall(r"<([^)]+)\(([^)]+)\)>", repr(v))[0] + self.assertEqual(oname, cname) + self.assertEqual(v.length, int(ovalue)) + + def test_subclassing(self): + class MyBufferProxy(BufferProxy): + def __repr__(self): + return "*%s*" % (BufferProxy.__repr__(self),) + + kwds = dict(self.view_keywords) + kwds["parent"] = 0 + v = MyBufferProxy(kwds) + self.assertEqual(v.parent, 0) + r = repr(v) + self.assertEqual(r[:2], "*<") + self.assertEqual(r[-2:], ">*") + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def NEWBUF_test_newbuf(self): + from ctypes import string_at + + from pygame.tests.test_utils import buftools + + Exporter = buftools.Exporter + Importer = buftools.Importer + exp = Exporter((10,), "B", readonly=True) + b = BufferProxy(exp) + self.assertEqual(b.length, exp.len) + self.assertEqual(b.raw, string_at(exp.buf, exp.len)) + d = b.__array_interface__ + try: + self.assertEqual(d["typestr"], "|u1") + self.assertEqual(d["shape"], exp.shape) + self.assertEqual(d["strides"], exp.strides) + self.assertEqual(d["data"], (exp.buf, True)) + finally: + d = None + exp = Exporter((3,), "=h") + b = BufferProxy(exp) + self.assertEqual(b.length, exp.len) + self.assertEqual(b.raw, string_at(exp.buf, exp.len)) + d = b.__array_interface__ + try: + lil_endian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN + f = "{}i{}".format("<" if lil_endian else ">", exp.itemsize) + self.assertEqual(d["typestr"], f) + self.assertEqual(d["shape"], exp.shape) + self.assertEqual(d["strides"], exp.strides) + self.assertEqual(d["data"], (exp.buf, False)) + finally: + d = None + + exp = Exporter((10, 2), "=i") + b = BufferProxy(exp) + imp = Importer(b, buftools.PyBUF_RECORDS) + self.assertTrue(imp.obj is b) + self.assertEqual(imp.buf, exp.buf) + self.assertEqual(imp.ndim, exp.ndim) + self.assertEqual(imp.format, exp.format) + self.assertEqual(imp.readonly, exp.readonly) + self.assertEqual(imp.itemsize, exp.itemsize) + self.assertEqual(imp.len, exp.len) + self.assertEqual(imp.shape, exp.shape) + self.assertEqual(imp.strides, exp.strides) + self.assertTrue(imp.suboffsets is None) + + d = { + "typestr": "|u1", + "shape": (10,), + "strides": (1,), + "data": (9, True), + } # 9? Will not reading the data anyway. + b = BufferProxy(d) + imp = Importer(b, buftools.PyBUF_SIMPLE) + self.assertTrue(imp.obj is b) + self.assertEqual(imp.buf, 9) + self.assertEqual(imp.len, 10) + self.assertEqual(imp.format, None) + self.assertEqual(imp.itemsize, 1) + self.assertEqual(imp.ndim, 0) + self.assertTrue(imp.readonly) + self.assertTrue(imp.shape is None) + self.assertTrue(imp.strides is None) + self.assertTrue(imp.suboffsets is None) + + try: + pygame.bufferproxy.get_segcount + except AttributeError: + pass + else: + + def test_oldbuf_arg(self): + self.OLDBUF_test_oldbuf_arg() + + def OLDBUF_test_oldbuf_arg(self): + from pygame.bufferproxy import get_segcount, get_read_buffer, get_write_buffer + + content = as_bytes("\x01\x00\x00\x02") * 12 + memory = ctypes.create_string_buffer(content) + memaddr = ctypes.addressof(memory) + + def raise_exception(o): + raise ValueError("An exception") + + bf = BufferProxy( + { + "shape": (len(content),), + "typestr": "|u1", + "data": (memaddr, False), + "strides": (1,), + } + ) + seglen, segaddr = get_read_buffer(bf, 0) + self.assertEqual(segaddr, 0) + self.assertEqual(seglen, 0) + seglen, segaddr = get_write_buffer(bf, 0) + self.assertEqual(segaddr, 0) + self.assertEqual(seglen, 0) + segcount, buflen = get_segcount(bf) + self.assertEqual(segcount, 1) + self.assertEqual(buflen, len(content)) + seglen, segaddr = get_read_buffer(bf, 0) + self.assertEqual(segaddr, memaddr) + self.assertEqual(seglen, len(content)) + seglen, segaddr = get_write_buffer(bf, 0) + self.assertEqual(segaddr, memaddr) + self.assertEqual(seglen, len(content)) + + bf = BufferProxy( + { + "shape": (len(content),), + "typestr": "|u1", + "data": (memaddr, True), + "strides": (1,), + } + ) + segcount, buflen = get_segcount(bf) + self.assertEqual(segcount, 1) + self.assertEqual(buflen, len(content)) + seglen, segaddr = get_read_buffer(bf, 0) + self.assertEqual(segaddr, memaddr) + self.assertEqual(seglen, len(content)) + self.assertRaises(ValueError, get_write_buffer, bf, 0) + + bf = BufferProxy( + { + "shape": (len(content),), + "typestr": "|u1", + "data": (memaddr, True), + "strides": (1,), + "before": raise_exception, + } + ) + segcount, buflen = get_segcount(bf) + self.assertEqual(segcount, 0) + self.assertEqual(buflen, 0) + + bf = BufferProxy( + { + "shape": (3, 4), + "typestr": "|u4", + "data": (memaddr, True), + "strides": (12, 4), + } + ) + segcount, buflen = get_segcount(bf) + self.assertEqual(segcount, 3 * 4) + self.assertEqual(buflen, 3 * 4 * 4) + for i in range(0, 4): + seglen, segaddr = get_read_buffer(bf, i) + self.assertEqual(segaddr, memaddr + i * 4) + self.assertEqual(seglen, 4) + + +class BufferProxyLegacyTest(unittest.TestCase): + content = as_bytes("\x01\x00\x00\x02") * 12 + buffer = ctypes.create_string_buffer(content) + data = (ctypes.addressof(buffer), True) + + def test_length(self): + + # __doc__ (as of 2008-08-02) for pygame.bufferproxy.BufferProxy.length: + + # The size of the buffer data in bytes. + bf = BufferProxy( + {"shape": (3, 4), "typestr": "|u4", "data": self.data, "strides": (12, 4)} + ) + self.assertEqual(bf.length, len(self.content)) + bf = BufferProxy( + {"shape": (3, 3), "typestr": "|u4", "data": self.data, "strides": (12, 4)} + ) + self.assertEqual(bf.length, 3 * 3 * 4) + + def test_raw(self): + + # __doc__ (as of 2008-08-02) for pygame.bufferproxy.BufferProxy.raw: + + # The raw buffer data as string. The string may contain NUL bytes. + + bf = BufferProxy( + {"shape": (len(self.content),), "typestr": "|u1", "data": self.data} + ) + self.assertEqual(bf.raw, self.content) + bf = BufferProxy( + {"shape": (3, 4), "typestr": "|u4", "data": self.data, "strides": (4, 12)} + ) + self.assertEqual(bf.raw, self.content) + bf = BufferProxy( + {"shape": (3, 4), "typestr": "|u1", "data": self.data, "strides": (16, 4)} + ) + self.assertRaises(ValueError, getattr, bf, "raw") + + def test_write(self): + + # __doc__ (as of 2008-08-02) for pygame.bufferproxy.BufferProxy.write: + + # B.write (bufferproxy, buffer, offset) -> None + # + # Writes raw data to the bufferproxy. + # + # Writes the raw data from buffer to the BufferProxy object, starting + # at the specified offset within the BufferProxy. + # If the length of the passed buffer exceeds the length of the + # BufferProxy (reduced by the offset), an IndexError will be raised. + from ctypes import c_byte, sizeof, addressof, string_at, memset + + nullbyte = "\x00".encode("latin_1") + Buf = c_byte * 10 + data_buf = Buf(*range(1, 3 * sizeof(Buf) + 1, 3)) + data = string_at(data_buf, sizeof(data_buf)) + buf = Buf() + bp = BufferProxy( + {"typestr": "|u1", "shape": (sizeof(buf),), "data": (addressof(buf), False)} + ) + try: + self.assertEqual(bp.raw, nullbyte * sizeof(Buf)) + bp.write(data) + self.assertEqual(bp.raw, data) + memset(buf, 0, sizeof(buf)) + bp.write(data[:3], 2) + raw = bp.raw + self.assertEqual(raw[:2], nullbyte * 2) + self.assertEqual(raw[2:5], data[:3]) + self.assertEqual(raw[5:], nullbyte * (sizeof(Buf) - 5)) + bp.write(data[:3], bp.length - 3) + raw = bp.raw + self.assertEqual(raw[-3:], data[:3]) + self.assertRaises(IndexError, bp.write, data, 1) + self.assertRaises(IndexError, bp.write, data[:5], -1) + self.assertRaises(IndexError, bp.write, data[:5], bp.length) + self.assertRaises(TypeError, bp.write, 12) + bp = BufferProxy( + { + "typestr": "|u1", + "shape": (sizeof(buf),), + "data": (addressof(buf), True), + } + ) + self.assertRaises(pygame.BufferError, bp.write, "123".encode("latin_1")) + finally: + # Make sure bp is garbage collected before buf + bp = None + gc.collect() + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/camera_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/camera_test.py new file mode 100644 index 0000000..8dfb45a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/camera_test.py @@ -0,0 +1,9 @@ +import unittest +import math + +import pygame +from pygame.compat import long_ + + +class CameraModuleTest(unittest.TestCase): + pass diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/cdrom_tags.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/cdrom_tags.py new file mode 100644 index 0000000..41756c7 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/cdrom_tags.py @@ -0,0 +1 @@ +__tags__ = ["interactive", "SDL2_ignore"] diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/cdrom_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/cdrom_test.py new file mode 100644 index 0000000..cc55078 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/cdrom_test.py @@ -0,0 +1,321 @@ +import unittest +from pygame.tests.test_utils import question, prompt + +import pygame + + +pygame.cdrom.init() +# The number of CD drives available for testing. +CD_DRIVE_COUNT = pygame.cdrom.get_count() +pygame.cdrom.quit() + + +class CDROMModuleTest(unittest.TestCase): + def setUp(self): + pygame.cdrom.init() + + def tearDown(self): + pygame.cdrom.quit() + + def todo_test_CD(self): + + # __doc__ (as of 2008-08-02) for pygame.cdrom.CD: + + # pygame.cdrom.CD(id): return CD + # class to manage a cdrom drive + # + # You can create a CD object for each cdrom on the system. Use + # pygame.cdrom.get_count() to determine how many drives actually + # exist. The id argument is an integer of the drive, starting at zero. + # + # The CD object is not initialized, you can only call CD.get_id() and + # CD.get_name() on an uninitialized drive. + # + # It is safe to create multiple CD objects for the same drive, they + # will all cooperate normally. + # + + self.fail() + + def test_get_count(self): + """Ensure the correct number of CD drives can be detected.""" + count = pygame.cdrom.get_count() + response = question( + "Is the correct number of CD drives on this " "system [{}]?".format(count) + ) + + self.assertTrue(response) + + def test_get_init(self): + """Ensure the initialization state can be retrieved.""" + self.assertTrue(pygame.cdrom.get_init()) + + def test_init(self): + """Ensure module still initialized after multiple init() calls.""" + pygame.cdrom.init() + pygame.cdrom.init() + + self.assertTrue(pygame.cdrom.get_init()) + + def test_quit(self): + """Ensure module not initialized after quit() called.""" + pygame.cdrom.quit() + + self.assertFalse(pygame.cdrom.get_init()) + + def test_quit__multiple(self): + """Ensure module still not initialized after multiple quit() calls.""" + pygame.cdrom.quit() + pygame.cdrom.quit() + + self.assertFalse(pygame.cdrom.get_init()) + + +@unittest.skipIf(0 == CD_DRIVE_COUNT, "No CD drives detected") +class CDTypeTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + pygame.cdrom.init() + + cls._cd_id = 0 # Only testing drive 0 for now. Expand in the future. + cls._cd = pygame.cdrom.CD(cls._cd_id) + + @classmethod + def tearDownClass(cls): + pygame.cdrom.quit() + + def setUp(self): + self._cd.init() + + def tearDown(self): + self._cd.quit() + + def test_eject(self): + """Ensure CD drive opens/ejects.""" + self._cd.eject() + response = question("Did the CD eject?") + + self.assertTrue(response) + + prompt("Please close the CD drive") + + def test_get_name(self): + """Ensure correct name for CD drive.""" + cd_name = self._cd.get_name() + response = question( + "Is the correct name for the CD drive [{}]?" "".format(cd_name) + ) + + self.assertTrue(response) + + def todo_test_get_all(self): + + # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.get_all: + + # CD.get_all(): return [(audio, start, end, lenth), ...] + # get all track information + # + # Return a list with information for every track on the cdrom. The + # information consists of a tuple with four values. The audio value is + # True if the track contains audio data. The start, end, and length + # values are floating point numbers in seconds. Start and end + # represent absolute times on the entire disc. + # + + self.fail() + + def todo_test_get_busy(self): + + # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.get_busy: + + # CD.get_busy(): return bool + # true if the drive is playing audio + # + # Returns True if the drive busy playing back audio. + + self.fail() + + def todo_test_get_current(self): + + # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.get_current: + + # CD.get_current(): return track, seconds + # the current audio playback position + # + # Returns both the current track and time of that track. This method + # works when the drive is either playing or paused. + # + # Note, track 0 is the first track on the CD. Track numbers start at zero. + + self.fail() + + def test_get_empty(self): + """Ensure correct name for CD drive.""" + prompt("Please ensure the CD drive is closed") + is_empty = self._cd.get_empty() + response = question("Is the CD drive empty?") + + self.assertEqual(is_empty, response) + + def test_get_id(self): + """Ensure the drive id/index is correct.""" + cd_id = self._cd.get_id() + + self.assertEqual(self._cd_id, cd_id) + + def test_get_init(self): + """Ensure the initialization state can be retrieved.""" + self.assertTrue(self._cd.get_init()) + + def todo_test_get_numtracks(self): + + # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.get_numtracks: + + # CD.get_numtracks(): return count + # the number of tracks on the cdrom + # + # Return the number of tracks on the cdrom in the drive. This will + # return zero of the drive is empty or has no tracks. + # + + self.fail() + + def todo_test_get_paused(self): + + # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.get_paused: + + # CD.get_paused(): return bool + # true if the drive is paused + # + # Returns True if the drive is currently paused. + + self.fail() + + def todo_test_get_track_audio(self): + + # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.get_track_audio: + + # CD.get_track_audio(track): return bool + # true if the cdrom track has audio data + # + # Determine if a track on a cdrom contains audio data. You can also + # call CD.num_tracks() and CD.get_all() to determine more information + # about the cdrom. + # + # Note, track 0 is the first track on the CD. Track numbers start at zero. + + self.fail() + + def todo_test_get_track_length(self): + + # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.get_track_length: + + # CD.get_track_length(track): return seconds + # length of a cdrom track + # + # Return a floating point value in seconds of the length of the cdrom track. + # Note, track 0 is the first track on the CD. Track numbers start at zero. + + self.fail() + + def todo_test_get_track_start(self): + + # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.get_track_start: + + # CD.get_track_start(track): return seconds + # start time of a cdrom track + # + # Return the absolute time in seconds where at start of the cdrom track. + # Note, track 0 is the first track on the CD. Track numbers start at zero. + + self.fail() + + def test_init(self): + """Ensure CD drive still initialized after multiple init() calls.""" + self._cd.init() + self._cd.init() + + self.assertTrue(self._cd.get_init()) + + def todo_test_pause(self): + + # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.pause: + + # CD.pause(): return None + # temporarily stop audio playback + # + # Temporarily stop audio playback on the CD. The playback can be + # resumed at the same point with the CD.resume() method. If the CD is + # not playing this method does nothing. + # + # Note, track 0 is the first track on the CD. Track numbers start at zero. + + self.fail() + + def todo_test_play(self): + + # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.play: + + # CD.init(): return None + # initialize a cdrom drive for use + # + # Playback audio from an audio cdrom in the drive. Besides the track + # number argument, you can also pass a starting and ending time for + # playback. The start and end time are in seconds, and can limit the + # section of an audio track played. + # + # If you pass a start time but no end, the audio will play to the end + # of the track. If you pass a start time and 'None' for the end time, + # the audio will play to the end of the entire disc. + # + # See the CD.get_numtracks() and CD.get_track_audio() to find tracks to playback. + # Note, track 0 is the first track on the CD. Track numbers start at zero. + + self.fail() + + def test_quit(self): + """Ensure CD drive not initialized after quit() called.""" + self._cd.quit() + + self.assertFalse(self._cd.get_init()) + + def test_quit__multiple(self): + """Ensure CD drive still not initialized after multiple quit() calls. + """ + self._cd.quit() + self._cd.quit() + + self.assertFalse(self._cd.get_init()) + + def todo_test_resume(self): + + # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.resume: + + # CD.resume(): return None + # unpause audio playback + # + # Unpause a paused CD. If the CD is not paused or already playing, + # this method does nothing. + # + + self.fail() + + def todo_test_stop(self): + + # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.stop: + + # CD.stop(): return None + # stop audio playback + # + # Stops playback of audio from the cdrom. This will also lose the + # current playback position. This method does nothing if the drive + # isn't already playing audio. + # + + self.fail() + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/color_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/color_test.py new file mode 100644 index 0000000..b9da01b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/color_test.py @@ -0,0 +1,1205 @@ +import unittest +import math +import operator +import platform + +import pygame +from pygame.compat import long_ +from pygame.colordict import THECOLORS + + +IS_PYPY = "PyPy" == platform.python_implementation() +################################### CONSTANTS ################################## + +rgba_vals = [0, 1, 62, 63, 126, 127, 255] + +rgba_combinations = [ + (r, g, b, a) + for r in rgba_vals + for g in rgba_vals + for b in rgba_vals + for a in rgba_vals +] + +################################################################################ + + +def rgba_combos_Color_generator(): + for rgba in rgba_combinations: + yield pygame.Color(*rgba) + + +# Python gamma correct +def gamma_correct(rgba_0_255, gamma): + corrected = round(255.0 * math.pow(rgba_0_255 / 255.0, gamma)) + return max(min(int(corrected), 255), 0) + + +################################################################################ + +# TODO: add tests for +# correct_gamma() -- test against statically defined verified correct values +# coerce () -- ?? + + +def _assignr(x, y): + x.r = y + + +def _assigng(x, y): + x.g = y + + +def _assignb(x, y): + x.b = y + + +def _assigna(x, y): + x.a = y + + +def _assign_item(x, p, y): + x[p] = y + + +class ColorTypeTest(unittest.TestCase): + def test_new(self): + c = pygame.Color.__new__(pygame.Color) + self.assertEqual(c, pygame.Color(0, 0, 0, 255)) + self.assertEqual(len(c), 4) + + def test_init(self): + c = pygame.Color(10, 20, 30, 200) + self.assertEqual(c, (10, 20, 30, 200)) + c.set_length(3) + self.assertEqual(len(c), 3) + c.__init__(100, 110, 120, 128) + self.assertEqual(len(c), 4) + self.assertEqual(c, (100, 110, 120, 128)) + + def test_invalid_html_hex_codes(self): + # This was a problem with the way 2 digit hex numbers were + # calculated. The test_hex_digits test is related to the fix. + Color = pygame.color.Color + self.assertRaises(ValueError, lambda: Color("# f000000")) + self.assertRaises(ValueError, lambda: Color("#f 000000")) + self.assertRaises(ValueError, lambda: Color("#-f000000")) + + def test_hex_digits(self): + # This is an implementation specific test. + # Two digit hex numbers are calculated using table lookups + # for the upper and lower digits. + Color = pygame.color.Color + self.assertEqual(Color("#00000000").r, 0x00) + self.assertEqual(Color("#10000000").r, 0x10) + self.assertEqual(Color("#20000000").r, 0x20) + self.assertEqual(Color("#30000000").r, 0x30) + self.assertEqual(Color("#40000000").r, 0x40) + self.assertEqual(Color("#50000000").r, 0x50) + self.assertEqual(Color("#60000000").r, 0x60) + self.assertEqual(Color("#70000000").r, 0x70) + self.assertEqual(Color("#80000000").r, 0x80) + self.assertEqual(Color("#90000000").r, 0x90) + self.assertEqual(Color("#A0000000").r, 0xA0) + self.assertEqual(Color("#B0000000").r, 0xB0) + self.assertEqual(Color("#C0000000").r, 0xC0) + self.assertEqual(Color("#D0000000").r, 0xD0) + self.assertEqual(Color("#E0000000").r, 0xE0) + self.assertEqual(Color("#F0000000").r, 0xF0) + self.assertEqual(Color("#01000000").r, 0x01) + self.assertEqual(Color("#02000000").r, 0x02) + self.assertEqual(Color("#03000000").r, 0x03) + self.assertEqual(Color("#04000000").r, 0x04) + self.assertEqual(Color("#05000000").r, 0x05) + self.assertEqual(Color("#06000000").r, 0x06) + self.assertEqual(Color("#07000000").r, 0x07) + self.assertEqual(Color("#08000000").r, 0x08) + self.assertEqual(Color("#09000000").r, 0x09) + self.assertEqual(Color("#0A000000").r, 0x0A) + self.assertEqual(Color("#0B000000").r, 0x0B) + self.assertEqual(Color("#0C000000").r, 0x0C) + self.assertEqual(Color("#0D000000").r, 0x0D) + self.assertEqual(Color("#0E000000").r, 0x0E) + self.assertEqual(Color("#0F000000").r, 0x0F) + + def test_comparison(self): + Color = pygame.color.Color + + # Check valid comparisons + self.assertTrue(Color(255, 0, 0, 0) == Color(255, 0, 0, 0)) + self.assertTrue(Color(0, 255, 0, 0) == Color(0, 255, 0, 0)) + self.assertTrue(Color(0, 0, 255, 0) == Color(0, 0, 255, 0)) + self.assertTrue(Color(0, 0, 0, 255) == Color(0, 0, 0, 255)) + self.assertFalse(Color(0, 0, 0, 0) == Color(255, 0, 0, 0)) + self.assertFalse(Color(0, 0, 0, 0) == Color(0, 255, 0, 0)) + self.assertFalse(Color(0, 0, 0, 0) == Color(0, 0, 255, 0)) + self.assertFalse(Color(0, 0, 0, 0) == Color(0, 0, 0, 255)) + self.assertTrue(Color(0, 0, 0, 0) != Color(255, 0, 0, 0)) + self.assertTrue(Color(0, 0, 0, 0) != Color(0, 255, 0, 0)) + self.assertTrue(Color(0, 0, 0, 0) != Color(0, 0, 255, 0)) + self.assertTrue(Color(0, 0, 0, 0) != Color(0, 0, 0, 255)) + self.assertFalse(Color(255, 0, 0, 0) != Color(255, 0, 0, 0)) + self.assertFalse(Color(0, 255, 0, 0) != Color(0, 255, 0, 0)) + self.assertFalse(Color(0, 0, 255, 0) != Color(0, 0, 255, 0)) + self.assertFalse(Color(0, 0, 0, 255) != Color(0, 0, 0, 255)) + + self.assertTrue(Color(255, 0, 0, 0) == (255, 0, 0, 0)) + self.assertTrue(Color(0, 255, 0, 0) == (0, 255, 0, 0)) + self.assertTrue(Color(0, 0, 255, 0) == (0, 0, 255, 0)) + self.assertTrue(Color(0, 0, 0, 255) == (0, 0, 0, 255)) + self.assertFalse(Color(0, 0, 0, 0) == (255, 0, 0, 0)) + self.assertFalse(Color(0, 0, 0, 0) == (0, 255, 0, 0)) + self.assertFalse(Color(0, 0, 0, 0) == (0, 0, 255, 0)) + self.assertFalse(Color(0, 0, 0, 0) == (0, 0, 0, 255)) + self.assertTrue(Color(0, 0, 0, 0) != (255, 0, 0, 0)) + self.assertTrue(Color(0, 0, 0, 0) != (0, 255, 0, 0)) + self.assertTrue(Color(0, 0, 0, 0) != (0, 0, 255, 0)) + self.assertTrue(Color(0, 0, 0, 0) != (0, 0, 0, 255)) + self.assertFalse(Color(255, 0, 0, 0) != (255, 0, 0, 0)) + self.assertFalse(Color(0, 255, 0, 0) != (0, 255, 0, 0)) + self.assertFalse(Color(0, 0, 255, 0) != (0, 0, 255, 0)) + self.assertFalse(Color(0, 0, 0, 255) != (0, 0, 0, 255)) + + self.assertTrue((255, 0, 0, 0) == Color(255, 0, 0, 0)) + self.assertTrue((0, 255, 0, 0) == Color(0, 255, 0, 0)) + self.assertTrue((0, 0, 255, 0) == Color(0, 0, 255, 0)) + self.assertTrue((0, 0, 0, 255) == Color(0, 0, 0, 255)) + self.assertFalse((0, 0, 0, 0) == Color(255, 0, 0, 0)) + self.assertFalse((0, 0, 0, 0) == Color(0, 255, 0, 0)) + self.assertFalse((0, 0, 0, 0) == Color(0, 0, 255, 0)) + self.assertFalse((0, 0, 0, 0) == Color(0, 0, 0, 255)) + self.assertTrue((0, 0, 0, 0) != Color(255, 0, 0, 0)) + self.assertTrue((0, 0, 0, 0) != Color(0, 255, 0, 0)) + self.assertTrue((0, 0, 0, 0) != Color(0, 0, 255, 0)) + self.assertTrue((0, 0, 0, 0) != Color(0, 0, 0, 255)) + self.assertFalse((255, 0, 0, 0) != Color(255, 0, 0, 0)) + self.assertFalse((0, 255, 0, 0) != Color(0, 255, 0, 0)) + self.assertFalse((0, 0, 255, 0) != Color(0, 0, 255, 0)) + self.assertFalse((0, 0, 0, 255) != Color(0, 0, 0, 255)) + + class TupleSubclass(tuple): + pass + + self.assertTrue(Color(255, 0, 0, 0) == TupleSubclass((255, 0, 0, 0))) + self.assertTrue(TupleSubclass((255, 0, 0, 0)) == Color(255, 0, 0, 0)) + self.assertFalse(Color(255, 0, 0, 0) != TupleSubclass((255, 0, 0, 0))) + self.assertFalse(TupleSubclass((255, 0, 0, 0)) != Color(255, 0, 0, 0)) + + # These are not supported so will be unequal. + self.assertFalse(Color(255, 0, 0, 0) == "#ff000000") + self.assertTrue(Color(255, 0, 0, 0) != "#ff000000") + + self.assertFalse("#ff000000" == Color(255, 0, 0, 0)) + self.assertTrue("#ff000000" != Color(255, 0, 0, 0)) + + self.assertFalse(Color(255, 0, 0, 0) == 0xFF000000) + self.assertTrue(Color(255, 0, 0, 0) != 0xFF000000) + + self.assertFalse(0xFF000000 == Color(255, 0, 0, 0)) + self.assertTrue(0xFF000000 != Color(255, 0, 0, 0)) + + self.assertFalse(Color(255, 0, 0, 0) == [255, 0, 0, 0]) + self.assertTrue(Color(255, 0, 0, 0) != [255, 0, 0, 0]) + + self.assertFalse([255, 0, 0, 0] == Color(255, 0, 0, 0)) + self.assertTrue([255, 0, 0, 0] != Color(255, 0, 0, 0)) + + # Comparison is not implemented for invalid color values. + class Test(object): + def __eq__(self, other): + return -1 + + def __ne__(self, other): + return -2 + + class TestTuple(tuple): + def __eq__(self, other): + return -1 + + def __ne__(self, other): + return -2 + + t = Test() + t_tuple = TestTuple(("a", 0, 0, 0)) + black = Color("black") + self.assertEqual(black == t, -1) + self.assertEqual(t == black, -1) + self.assertEqual(black != t, -2) + self.assertEqual(t != black, -2) + self.assertEqual(black == t_tuple, -1) + self.assertEqual(black != t_tuple, -2) + self.assertEqual(t_tuple == black, -1) + self.assertEqual(t_tuple != black, -2) + + def test_ignore_whitespace(self): + self.assertEqual(pygame.color.Color("red"), pygame.color.Color(" r e d ")) + + def test_slice(self): + # """|tags: python3_ignore|""" + + # slicing a color gives you back a tuple. + # do all sorts of slice combinations. + c = pygame.Color(1, 2, 3, 4) + + self.assertEqual((1, 2, 3, 4), c[:]) + self.assertEqual((1, 2, 3), c[:-1]) + + self.assertEqual((), c[:-5]) + + self.assertEqual((1, 2, 3, 4), c[:4]) + self.assertEqual((1, 2, 3, 4), c[:5]) + self.assertEqual((1, 2), c[:2]) + self.assertEqual((1,), c[:1]) + self.assertEqual((), c[:0]) + + self.assertEqual((2,), c[1:-2]) + self.assertEqual((3, 4), c[-2:]) + self.assertEqual((4,), c[-1:]) + + # NOTE: assigning to a slice is currently unsupported. + + def test_unpack(self): + # should be able to unpack to r,g,b,a and r,g,b + c = pygame.Color(1, 2, 3, 4) + r, g, b, a = c + self.assertEqual((1, 2, 3, 4), (r, g, b, a)) + self.assertEqual(c, (r, g, b, a)) + + c.set_length(3) + r, g, b = c + self.assertEqual((1, 2, 3), (r, g, b)) + + def test_length(self): + # should be able to unpack to r,g,b,a and r,g,b + c = pygame.Color(1, 2, 3, 4) + self.assertEqual(len(c), 4) + + c.set_length(3) + self.assertEqual(len(c), 3) + + # it keeps the old alpha anyway... + self.assertEqual(c.a, 4) + + # however you can't get the alpha in this way: + self.assertRaises(IndexError, lambda x: c[x], 4) + + c.set_length(4) + self.assertEqual(len(c), 4) + self.assertEqual(len(c), 4) + + self.assertRaises(ValueError, c.set_length, 5) + self.assertRaises(ValueError, c.set_length, -1) + self.assertRaises(ValueError, c.set_length, 0) + self.assertRaises(ValueError, c.set_length, pow(2, long_(33))) + + def test_case_insensitivity_of_string_args(self): + self.assertEqual(pygame.color.Color("red"), pygame.color.Color("Red")) + + def test_color(self): + """Ensures Color objects can be created.""" + color = pygame.Color(0, 0, 0, 0) + + self.assertIsInstance(color, pygame.Color) + + def test_color__rgba_int_args(self): + """Ensures Color objects can be created using ints.""" + color = pygame.Color(10, 20, 30, 40) + + self.assertEqual(color.r, 10) + self.assertEqual(color.g, 20) + self.assertEqual(color.b, 30) + self.assertEqual(color.a, 40) + + def test_color__rgba_int_args_without_alpha(self): + """Ensures Color objects can be created without providing alpha.""" + color = pygame.Color(10, 20, 30) + + self.assertEqual(color.r, 10) + self.assertEqual(color.g, 20) + self.assertEqual(color.b, 30) + self.assertEqual(color.a, 255) + + def test_color__rgba_int_args_invalid_value(self): + """Ensures invalid values are detected when creating Color objects.""" + self.assertRaises(ValueError, pygame.Color, 257, 10, 105, 44) + self.assertRaises(ValueError, pygame.Color, 10, 257, 105, 44) + self.assertRaises(ValueError, pygame.Color, 10, 105, 257, 44) + self.assertRaises(ValueError, pygame.Color, 10, 105, 44, 257) + + def test_color__rgba_int_args_invalid_value_without_alpha(self): + """Ensures invalid values are detected when creating Color objects + without providing an alpha. + """ + self.assertRaises(ValueError, pygame.Color, 256, 10, 105) + self.assertRaises(ValueError, pygame.Color, 10, 256, 105) + self.assertRaises(ValueError, pygame.Color, 10, 105, 256) + + def test_color__color_object_arg(self): + """Ensures Color objects can be created using Color objects.""" + color_args = (10, 20, 30, 40) + color_obj = pygame.Color(*color_args) + + new_color_obj = pygame.Color(color_obj) + + self.assertIsInstance(new_color_obj, pygame.Color) + self.assertEqual(new_color_obj, color_obj) + self.assertEqual(new_color_obj.r, color_args[0]) + self.assertEqual(new_color_obj.g, color_args[1]) + self.assertEqual(new_color_obj.b, color_args[2]) + self.assertEqual(new_color_obj.a, color_args[3]) + + def test_color__name_str_arg(self): + """Ensures Color objects can be created using str names.""" + for name in ("aquamarine3", "AQUAMARINE3", "AqUAmArIne3"): + color = pygame.Color(name) + + self.assertEqual(color.r, 102) + self.assertEqual(color.g, 205) + self.assertEqual(color.b, 170) + self.assertEqual(color.a, 255) + + def test_color__name_str_arg_from_colordict(self): + """Ensures Color objects can be created using str names + from the THECOLORS dict.""" + for name, values in THECOLORS.items(): + color = pygame.Color(name) + + self.assertEqual(color.r, values[0]) + self.assertEqual(color.g, values[1]) + self.assertEqual(color.b, values[2]) + self.assertEqual(color.a, values[3]) + + def test_color__html_str_arg(self): + """Ensures Color objects can be created using html strings.""" + # See test_webstyle() for related tests. + color = pygame.Color("#a1B2c3D4") + + self.assertEqual(color.r, 0xA1) + self.assertEqual(color.g, 0xB2) + self.assertEqual(color.b, 0xC3) + self.assertEqual(color.a, 0xD4) + + def test_color__hex_str_arg(self): + """Ensures Color objects can be created using hex strings.""" + # See test_webstyle() for related tests. + color = pygame.Color("0x1a2B3c4D") + + self.assertEqual(color.r, 0x1A) + self.assertEqual(color.g, 0x2B) + self.assertEqual(color.b, 0x3C) + self.assertEqual(color.a, 0x4D) + + def test_color__int_arg(self): + """Ensures Color objects can be created using one int value.""" + for value in (0x0, 0xFFFFFFFF, 0xAABBCCDD): + color = pygame.Color(value) + + self.assertEqual(color.r, (value >> 24) & 0xFF) + self.assertEqual(color.g, (value >> 16) & 0xFF) + self.assertEqual(color.b, (value >> 8) & 0xFF) + self.assertEqual(color.a, value & 0xFF) + + def test_color__int_arg_invalid(self): + """Ensures invalid int values are detected when creating Color objects. + """ + with self.assertRaises(ValueError): + color = pygame.Color(0x1FFFFFFFF) + + def test_color__sequence_arg(self): + """Ensures Color objects can be created using tuples/lists.""" + color_values = (33, 44, 55, 66) + for seq_type in (tuple, list): + color = pygame.Color(seq_type(color_values)) + + self.assertEqual(color.r, color_values[0]) + self.assertEqual(color.g, color_values[1]) + self.assertEqual(color.b, color_values[2]) + self.assertEqual(color.a, color_values[3]) + + def test_color__sequence_arg_without_alpha(self): + """Ensures Color objects can be created using tuples/lists + without providing an alpha value. + """ + color_values = (33, 44, 55) + for seq_type in (tuple, list): + color = pygame.Color(seq_type(color_values)) + + self.assertEqual(color.r, color_values[0]) + self.assertEqual(color.g, color_values[1]) + self.assertEqual(color.b, color_values[2]) + self.assertEqual(color.a, 255) + + def test_color__sequence_arg_invalid_value(self): + """Ensures invalid sequences are detected when creating Color objects. + """ + cls = pygame.Color + for seq_type in (tuple, list): + self.assertRaises(ValueError, cls, seq_type((256, 90, 80, 70))) + self.assertRaises(ValueError, cls, seq_type((100, 256, 80, 70))) + self.assertRaises(ValueError, cls, seq_type((100, 90, 256, 70))) + self.assertRaises(ValueError, cls, seq_type((100, 90, 80, 256))) + + def test_color__sequence_arg_invalid_value_without_alpha(self): + """Ensures invalid sequences are detected when creating Color objects + without providing an alpha. + """ + cls = pygame.Color + for seq_type in (tuple, list): + self.assertRaises(ValueError, cls, seq_type((256, 90, 80))) + self.assertRaises(ValueError, cls, seq_type((100, 256, 80))) + self.assertRaises(ValueError, cls, seq_type((100, 90, 256))) + + def test_color__sequence_arg_invalid_format(self): + """Ensures invalid sequences are detected when creating Color objects + with the wrong number of values. + """ + cls = pygame.Color + for seq_type in (tuple, list): + self.assertRaises(ValueError, cls, seq_type((100,))) + self.assertRaises(ValueError, cls, seq_type((100, 90))) + self.assertRaises(ValueError, cls, seq_type((100, 90, 80, 70, 60))) + + def test_rgba(self): + c = pygame.Color(0) + self.assertEqual(c.r, 0) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 0) + self.assertEqual(c.a, 0) + + # Test simple assignments + c.r = 123 + self.assertEqual(c.r, 123) + self.assertRaises(ValueError, _assignr, c, 537) + self.assertEqual(c.r, 123) + self.assertRaises(ValueError, _assignr, c, -3) + self.assertEqual(c.r, 123) + + c.g = 55 + self.assertEqual(c.g, 55) + self.assertRaises(ValueError, _assigng, c, 348) + self.assertEqual(c.g, 55) + self.assertRaises(ValueError, _assigng, c, -44) + self.assertEqual(c.g, 55) + + c.b = 77 + self.assertEqual(c.b, 77) + self.assertRaises(ValueError, _assignb, c, 256) + self.assertEqual(c.b, 77) + self.assertRaises(ValueError, _assignb, c, -12) + self.assertEqual(c.b, 77) + + c.a = 255 + self.assertEqual(c.a, 255) + self.assertRaises(ValueError, _assigna, c, 312) + self.assertEqual(c.a, 255) + self.assertRaises(ValueError, _assigna, c, -10) + self.assertEqual(c.a, 255) + + def test_repr(self): + c = pygame.Color(68, 38, 26, 69) + t = "(68, 38, 26, 69)" + self.assertEqual(repr(c), t) + + def test_add(self): + c1 = pygame.Color(0) + self.assertEqual(c1.r, 0) + self.assertEqual(c1.g, 0) + self.assertEqual(c1.b, 0) + self.assertEqual(c1.a, 0) + + c2 = pygame.Color(20, 33, 82, 193) + self.assertEqual(c2.r, 20) + self.assertEqual(c2.g, 33) + self.assertEqual(c2.b, 82) + self.assertEqual(c2.a, 193) + + c3 = c1 + c2 + self.assertEqual(c3.r, 20) + self.assertEqual(c3.g, 33) + self.assertEqual(c3.b, 82) + self.assertEqual(c3.a, 193) + + c3 = c3 + c2 + self.assertEqual(c3.r, 40) + self.assertEqual(c3.g, 66) + self.assertEqual(c3.b, 164) + self.assertEqual(c3.a, 255) + + # Issue #286: Is type checking done for Python 3.x? + self.assertRaises(TypeError, operator.add, c1, None) + self.assertRaises(TypeError, operator.add, None, c1) + + def test_sub(self): + c1 = pygame.Color(0xFFFFFFFF) + self.assertEqual(c1.r, 255) + self.assertEqual(c1.g, 255) + self.assertEqual(c1.b, 255) + self.assertEqual(c1.a, 255) + + c2 = pygame.Color(20, 33, 82, 193) + self.assertEqual(c2.r, 20) + self.assertEqual(c2.g, 33) + self.assertEqual(c2.b, 82) + self.assertEqual(c2.a, 193) + + c3 = c1 - c2 + self.assertEqual(c3.r, 235) + self.assertEqual(c3.g, 222) + self.assertEqual(c3.b, 173) + self.assertEqual(c3.a, 62) + + c3 = c3 - c2 + self.assertEqual(c3.r, 215) + self.assertEqual(c3.g, 189) + self.assertEqual(c3.b, 91) + self.assertEqual(c3.a, 0) + + # Issue #286: Is type checking done for Python 3.x? + self.assertRaises(TypeError, operator.sub, c1, None) + self.assertRaises(TypeError, operator.sub, None, c1) + + def test_mul(self): + c1 = pygame.Color(0x01010101) + self.assertEqual(c1.r, 1) + self.assertEqual(c1.g, 1) + self.assertEqual(c1.b, 1) + self.assertEqual(c1.a, 1) + + c2 = pygame.Color(2, 5, 3, 22) + self.assertEqual(c2.r, 2) + self.assertEqual(c2.g, 5) + self.assertEqual(c2.b, 3) + self.assertEqual(c2.a, 22) + + c3 = c1 * c2 + self.assertEqual(c3.r, 2) + self.assertEqual(c3.g, 5) + self.assertEqual(c3.b, 3) + self.assertEqual(c3.a, 22) + + c3 = c3 * c2 + self.assertEqual(c3.r, 4) + self.assertEqual(c3.g, 25) + self.assertEqual(c3.b, 9) + self.assertEqual(c3.a, 255) + + # Issue #286: Is type checking done for Python 3.x? + self.assertRaises(TypeError, operator.mul, c1, None) + self.assertRaises(TypeError, operator.mul, None, c1) + + def test_div(self): + c1 = pygame.Color(0x80808080) + self.assertEqual(c1.r, 128) + self.assertEqual(c1.g, 128) + self.assertEqual(c1.b, 128) + self.assertEqual(c1.a, 128) + + c2 = pygame.Color(2, 4, 8, 16) + self.assertEqual(c2.r, 2) + self.assertEqual(c2.g, 4) + self.assertEqual(c2.b, 8) + self.assertEqual(c2.a, 16) + + c3 = c1 // c2 + self.assertEqual(c3.r, 64) + self.assertEqual(c3.g, 32) + self.assertEqual(c3.b, 16) + self.assertEqual(c3.a, 8) + + c3 = c3 // c2 + self.assertEqual(c3.r, 32) + self.assertEqual(c3.g, 8) + self.assertEqual(c3.b, 2) + self.assertEqual(c3.a, 0) + + # Issue #286: Is type checking done for Python 3.x? + self.assertRaises(TypeError, operator.floordiv, c1, None) + self.assertRaises(TypeError, operator.floordiv, None, c1) + + # Division by zero check + dividend = pygame.Color(255, 255, 255, 255) + for i in range(4): + divisor = pygame.Color(64, 64, 64, 64) + divisor[i] = 0 + quotient = pygame.Color(3, 3, 3, 3) + quotient[i] = 0 + self.assertEqual(dividend // divisor, quotient) + + def test_mod(self): + c1 = pygame.Color(0xFFFFFFFF) + self.assertEqual(c1.r, 255) + self.assertEqual(c1.g, 255) + self.assertEqual(c1.b, 255) + self.assertEqual(c1.a, 255) + + c2 = pygame.Color(2, 4, 8, 16) + self.assertEqual(c2.r, 2) + self.assertEqual(c2.g, 4) + self.assertEqual(c2.b, 8) + self.assertEqual(c2.a, 16) + + c3 = c1 % c2 + self.assertEqual(c3.r, 1) + self.assertEqual(c3.g, 3) + self.assertEqual(c3.b, 7) + self.assertEqual(c3.a, 15) + + # Issue #286: Is type checking done for Python 3.x? + self.assertRaises(TypeError, operator.mod, c1, None) + self.assertRaises(TypeError, operator.mod, None, c1) + + # Division by zero check + dividend = pygame.Color(255, 255, 255, 255) + for i in range(4): + divisor = pygame.Color(64, 64, 64, 64) + divisor[i] = 0 + quotient = pygame.Color(63, 63, 63, 63) + quotient[i] = 0 + self.assertEqual(dividend % divisor, quotient) + + def test_float(self): + c = pygame.Color(0xCC00CC00) + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 0) + self.assertEqual(float(c), float(0xCC00CC00)) + + c = pygame.Color(0x33727592) + self.assertEqual(c.r, 51) + self.assertEqual(c.g, 114) + self.assertEqual(c.b, 117) + self.assertEqual(c.a, 146) + self.assertEqual(float(c), float(0x33727592)) + + def test_oct(self): + c = pygame.Color(0xCC00CC00) + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 0) + self.assertEqual(oct(c), oct(0xCC00CC00)) + + c = pygame.Color(0x33727592) + self.assertEqual(c.r, 51) + self.assertEqual(c.g, 114) + self.assertEqual(c.b, 117) + self.assertEqual(c.a, 146) + self.assertEqual(oct(c), oct(0x33727592)) + + def test_hex(self): + c = pygame.Color(0xCC00CC00) + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 0) + self.assertEqual(hex(c), hex(0xCC00CC00)) + + c = pygame.Color(0x33727592) + self.assertEqual(c.r, 51) + self.assertEqual(c.g, 114) + self.assertEqual(c.b, 117) + self.assertEqual(c.a, 146) + self.assertEqual(hex(c), hex(0x33727592)) + + def test_webstyle(self): + c = pygame.Color("#CC00CC11") + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 17) + self.assertEqual(hex(c), hex(0xCC00CC11)) + + c = pygame.Color("#CC00CC") + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 255) + self.assertEqual(hex(c), hex(0xCC00CCFF)) + + c = pygame.Color("0xCC00CC11") + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 17) + self.assertEqual(hex(c), hex(0xCC00CC11)) + + c = pygame.Color("0xCC00CC") + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 255) + self.assertEqual(hex(c), hex(0xCC00CCFF)) + + self.assertRaises(ValueError, pygame.Color, "#cc00qq") + self.assertRaises(ValueError, pygame.Color, "0xcc00qq") + self.assertRaises(ValueError, pygame.Color, "09abcdef") + self.assertRaises(ValueError, pygame.Color, "09abcde") + self.assertRaises(ValueError, pygame.Color, "quarky") + + def test_int(self): + # This will be a long + c = pygame.Color(0xCC00CC00) + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 0) + self.assertEqual(int(c), int(0xCC00CC00)) + + # This will be an int + c = pygame.Color(0x33727592) + self.assertEqual(c.r, 51) + self.assertEqual(c.g, 114) + self.assertEqual(c.b, 117) + self.assertEqual(c.a, 146) + self.assertEqual(int(c), int(0x33727592)) + + def test_long(self): + # This will be a long + c = pygame.Color(0xCC00CC00) + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 0) + self.assertEqual(long_(c), long_(0xCC00CC00)) + + # This will be an int + c = pygame.Color(0x33727592) + self.assertEqual(c.r, 51) + self.assertEqual(c.g, 114) + self.assertEqual(c.b, 117) + self.assertEqual(c.a, 146) + self.assertEqual(long_(c), long_(0x33727592)) + + def test_normalize(self): + c = pygame.Color(204, 38, 194, 55) + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 38) + self.assertEqual(c.b, 194) + self.assertEqual(c.a, 55) + + t = c.normalize() + + self.assertAlmostEqual(t[0], 0.800000, 5) + self.assertAlmostEqual(t[1], 0.149016, 5) + self.assertAlmostEqual(t[2], 0.760784, 5) + self.assertAlmostEqual(t[3], 0.215686, 5) + + def test_len(self): + c = pygame.Color(204, 38, 194, 55) + self.assertEqual(len(c), 4) + + def test_get_item(self): + c = pygame.Color(204, 38, 194, 55) + self.assertEqual(c[0], 204) + self.assertEqual(c[1], 38) + self.assertEqual(c[2], 194) + self.assertEqual(c[3], 55) + + def test_set_item(self): + c = pygame.Color(204, 38, 194, 55) + self.assertEqual(c[0], 204) + self.assertEqual(c[1], 38) + self.assertEqual(c[2], 194) + self.assertEqual(c[3], 55) + + c[0] = 33 + self.assertEqual(c[0], 33) + c[1] = 48 + self.assertEqual(c[1], 48) + c[2] = 173 + self.assertEqual(c[2], 173) + c[3] = 213 + self.assertEqual(c[3], 213) + + # Now try some 'invalid' ones + self.assertRaises(TypeError, _assign_item, c, 0, 95.485) + self.assertEqual(c[0], 33) + self.assertRaises(ValueError, _assign_item, c, 1, -83) + self.assertEqual(c[1], 48) + self.assertRaises(TypeError, _assign_item, c, 2, "Hello") + self.assertEqual(c[2], 173) + + def test_Color_type_works_for_Surface_get_and_set_colorkey(self): + s = pygame.Surface((32, 32)) + + c = pygame.Color(33, 22, 11, 255) + s.set_colorkey(c) + + get_r, get_g, get_b, get_a = s.get_colorkey() + + self.assertTrue(get_r == c.r) + self.assertTrue(get_g == c.g) + self.assertTrue(get_b == c.b) + self.assertTrue(get_a == c.a) + + ########## HSLA, HSVA, CMY, I1I2I3 ALL ELEMENTS WITHIN SPECIFIED RANGE ######### + + def test_hsla__all_elements_within_limits(self): + for c in rgba_combos_Color_generator(): + h, s, l, a = c.hsla + self.assertTrue(0 <= h <= 360) + self.assertTrue(0 <= s <= 100) + self.assertTrue(0 <= l <= 100) + self.assertTrue(0 <= a <= 100) + + def test_hsva__all_elements_within_limits(self): + for c in rgba_combos_Color_generator(): + h, s, v, a = c.hsva + self.assertTrue(0 <= h <= 360) + self.assertTrue(0 <= s <= 100) + self.assertTrue(0 <= v <= 100) + self.assertTrue(0 <= a <= 100) + + def test_cmy__all_elements_within_limits(self): + for c in rgba_combos_Color_generator(): + c, m, y = c.cmy + self.assertTrue(0 <= c <= 1) + self.assertTrue(0 <= m <= 1) + self.assertTrue(0 <= y <= 1) + + def test_i1i2i3__all_elements_within_limits(self): + for c in rgba_combos_Color_generator(): + i1, i2, i3 = c.i1i2i3 + self.assertTrue(0 <= i1 <= 1) + self.assertTrue(-0.5 <= i2 <= 0.5) + self.assertTrue(-0.5 <= i3 <= 0.5) + + def test_issue_269(self): + """PyColor OverflowError on HSVA with hue value of 360 + + >>> c = pygame.Color(0) + >>> c.hsva = (360,0,0,0) + Traceback (most recent call last): + File "", line 1, in + OverflowError: this is not allowed to happen ever + >>> pygame.ver + '1.9.1release' + >>> + + """ + + c = pygame.Color(0) + c.hsva = 360, 0, 0, 0 + self.assertEqual(c.hsva, (0, 0, 0, 0)) + c.hsva = 360, 100, 100, 100 + self.assertEqual(c.hsva, (0, 100, 100, 100)) + self.assertEqual(c, (255, 0, 0, 255)) + + ####################### COLORSPACE PROPERTY SANITY TESTS ####################### + + def colorspaces_converted_should_not_raise(self, prop): + fails = 0 + + x = 0 + for c in rgba_combos_Color_generator(): + x += 1 + + other = pygame.Color(0) + + try: + setattr(other, prop, getattr(c, prop)) + # eg other.hsla = c.hsla + + except ValueError: + fails += 1 + + self.assertTrue(x > 0, "x is combination counter, 0 means no tests!") + self.assertTrue((fails, x) == (0, x)) + + def test_hsla__sanity_testing_converted_should_not_raise(self): + self.colorspaces_converted_should_not_raise("hsla") + + def test_hsva__sanity_testing_converted_should_not_raise(self): + self.colorspaces_converted_should_not_raise("hsva") + + def test_cmy__sanity_testing_converted_should_not_raise(self): + self.colorspaces_converted_should_not_raise("cmy") + + def test_i1i2i3__sanity_testing_converted_should_not_raise(self): + self.colorspaces_converted_should_not_raise("i1i2i3") + + ################################################################################ + + def colorspaces_converted_should_equate_bar_rounding(self, prop): + for c in rgba_combos_Color_generator(): + other = pygame.Color(0) + + try: + setattr(other, prop, getattr(c, prop)) + # eg other.hsla = c.hsla + + self.assertTrue(abs(other.r - c.r) <= 1) + self.assertTrue(abs(other.b - c.b) <= 1) + self.assertTrue(abs(other.g - c.g) <= 1) + # CMY and I1I2I3 do not care about the alpha + if not prop in ("cmy", "i1i2i3"): + self.assertTrue(abs(other.a - c.a) <= 1) + + except ValueError: + pass # other tests will notify, this tests equation + + def test_hsla__sanity_testing_converted_should_equate_bar_rounding(self): + self.colorspaces_converted_should_equate_bar_rounding("hsla") + + def test_hsva__sanity_testing_converted_should_equate_bar_rounding(self): + self.colorspaces_converted_should_equate_bar_rounding("hsva") + + def test_cmy__sanity_testing_converted_should_equate_bar_rounding(self): + self.colorspaces_converted_should_equate_bar_rounding("cmy") + + def test_i1i2i3__sanity_testing_converted_should_equate_bar_rounding(self): + self.colorspaces_converted_should_equate_bar_rounding("i1i2i3") + + ################################################################################ + + def test_correct_gamma__verified_against_python_implementation(self): + "|tags:slow|" + # gamma_correct defined at top of page + + gammas = [i / 10.0 for i in range(1, 31)] # [0.1 ... 3.0] + gammas_len = len(gammas) + + for i, c in enumerate(rgba_combos_Color_generator()): + gamma = gammas[i % gammas_len] + + corrected = pygame.Color(*[gamma_correct(x, gamma) for x in tuple(c)]) + lib_corrected = c.correct_gamma(gamma) + + self.assertTrue(corrected.r == lib_corrected.r) + self.assertTrue(corrected.g == lib_corrected.g) + self.assertTrue(corrected.b == lib_corrected.b) + self.assertTrue(corrected.a == lib_corrected.a) + + # TODO: test against statically defined verified _correct_ values + # assert corrected.r == 125 etc. + + def test_pickle(self): + import pickle + + c1 = pygame.Color(1, 2, 3, 4) + # c2 = pygame.Color(255,254,253,252) + pickle_string = pickle.dumps(c1) + c1_frompickle = pickle.loads(pickle_string) + self.assertEqual(c1, c1_frompickle) + + ################################################################################ + # only available if ctypes module is also available + + @unittest.skipIf(IS_PYPY, "PyPy has no ctypes") + def test_arraystruct(self): + + import pygame.tests.test_utils.arrinter as ai + import ctypes as ct + + c_byte_p = ct.POINTER(ct.c_byte) + c = pygame.Color(5, 7, 13, 23) + flags = ai.PAI_CONTIGUOUS | ai.PAI_FORTRAN | ai.PAI_ALIGNED | ai.PAI_NOTSWAPPED + for i in range(1, 5): + c.set_length(i) + inter = ai.ArrayInterface(c) + self.assertEqual(inter.two, 2) + self.assertEqual(inter.nd, 1) + self.assertEqual(inter.typekind, "u") + self.assertEqual(inter.itemsize, 1) + self.assertEqual(inter.flags, flags) + self.assertEqual(inter.shape[0], i) + self.assertEqual(inter.strides[0], 1) + data = ct.cast(inter.data, c_byte_p) + for j in range(i): + self.assertEqual(data[j], c[j]) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_newbuf(self): + from pygame.tests.test_utils import buftools + from ctypes import cast, POINTER, c_uint8 + + class ColorImporter(buftools.Importer): + def __init__(self, color, flags): + super(ColorImporter, self).__init__(color, flags) + self.items = cast(self.buf, POINTER(c_uint8)) + + def __getitem__(self, index): + if 0 <= index < 4: + return self.items[index] + raise IndexError( + "valid index values are between 0 and 3: " "got {}".format(index) + ) + + def __setitem__(self, index, value): + if 0 <= index < 4: + self.items[index] = value + else: + raise IndexError( + "valid index values are between 0 and 3: " + "got {}".format(index) + ) + + c = pygame.Color(50, 100, 150, 200) + imp = ColorImporter(c, buftools.PyBUF_SIMPLE) + self.assertTrue(imp.obj is c) + self.assertEqual(imp.ndim, 0) + self.assertEqual(imp.itemsize, 1) + self.assertEqual(imp.len, 4) + self.assertTrue(imp.readonly) + self.assertTrue(imp.format is None) + self.assertTrue(imp.shape is None) + self.assertTrue(imp.strides is None) + self.assertTrue(imp.suboffsets is None) + for i in range(4): + self.assertEqual(c[i], imp[i]) + imp[0] = 60 + self.assertEqual(c.r, 60) + imp[1] = 110 + self.assertEqual(c.g, 110) + imp[2] = 160 + self.assertEqual(c.b, 160) + imp[3] = 210 + self.assertEqual(c.a, 210) + imp = ColorImporter(c, buftools.PyBUF_FORMAT) + self.assertEqual(imp.ndim, 0) + self.assertEqual(imp.itemsize, 1) + self.assertEqual(imp.len, 4) + self.assertEqual(imp.format, "B") + self.assertEqual(imp.ndim, 0) + self.assertEqual(imp.itemsize, 1) + self.assertEqual(imp.len, 4) + imp = ColorImporter(c, buftools.PyBUF_ND) + self.assertEqual(imp.ndim, 1) + self.assertEqual(imp.itemsize, 1) + self.assertEqual(imp.len, 4) + self.assertTrue(imp.format is None) + self.assertEqual(imp.shape, (4,)) + self.assertEqual(imp.strides, None) + imp = ColorImporter(c, buftools.PyBUF_STRIDES) + self.assertEqual(imp.ndim, 1) + self.assertTrue(imp.format is None) + self.assertEqual(imp.shape, (4,)) + self.assertEqual(imp.strides, (1,)) + imp = ColorImporter(c, buftools.PyBUF_C_CONTIGUOUS) + self.assertEqual(imp.ndim, 1) + imp = ColorImporter(c, buftools.PyBUF_F_CONTIGUOUS) + self.assertEqual(imp.ndim, 1) + imp = ColorImporter(c, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertEqual(imp.ndim, 1) + for i in range(1, 5): + c.set_length(i) + imp = ColorImporter(c, buftools.PyBUF_ND) + self.assertEqual(imp.ndim, 1) + self.assertEqual(imp.len, i) + self.assertEqual(imp.shape, (i,)) + self.assertRaises(BufferError, ColorImporter, c, buftools.PyBUF_WRITABLE) + + def test_lerp(self): + # setup + Color = pygame.color.Color + + color0 = Color(0, 0, 0, 0) + color128 = Color(128, 128, 128, 128) + color255 = Color(255, 255, 255, 255) + color100 = Color(100, 100, 100, 100) + + # type checking + self.assertTrue(isinstance(color0.lerp(color128, 0.5), Color)) + + # common value testing + self.assertEqual(color0.lerp(color128, 0.5), Color(64, 64, 64, 64)) + self.assertEqual(color0.lerp(color128, 0.5), Color(64, 64, 64, 64)) + self.assertEqual(color128.lerp(color255, 0.5), Color(192, 192, 192, 192)) + self.assertEqual(color0.lerp(color255, 0.5), Color(128, 128, 128, 128)) + + # testing extremes + self.assertEqual(color0.lerp(color100, 0), color0) + self.assertEqual(color0.lerp(color100, 0.01), Color(1, 1, 1, 1)) + self.assertEqual(color0.lerp(color100, 0.99), Color(99, 99, 99, 99)) + self.assertEqual(color0.lerp(color100, 1), color100) + + # kwarg testing + self.assertEqual(color0.lerp(color=color100, amount=0.5), Color(50, 50, 50, 50)) + self.assertEqual(color0.lerp(amount=0.5, color=color100), Color(50, 50, 50, 50)) + + # invalid input testing + self.assertRaises(ValueError, lambda: color0.lerp(color128, 2.5)) + self.assertRaises(ValueError, lambda: color0.lerp(color128, -0.5)) + self.assertRaises(ValueError, lambda: color0.lerp((256, 0, 0, 0), 0.5)) + self.assertRaises(ValueError, lambda: color0.lerp((0, 256, 0, 0), 0.5)) + self.assertRaises(ValueError, lambda: color0.lerp((0, 0, 256, 0), 0.5)) + self.assertRaises(ValueError, lambda: color0.lerp((0, 0, 0, 256), 0.5)) + self.assertRaises(TypeError, lambda: color0.lerp(0.2, 0.5)) + + +class SubclassTest(unittest.TestCase): + class MyColor(pygame.Color): + def __init__(self, *args, **kwds): + super(SubclassTest.MyColor, self).__init__(*args, **kwds) + self.an_attribute = True + + def test_add(self): + mc1 = self.MyColor(128, 128, 128, 255) + self.assertTrue(mc1.an_attribute) + c2 = pygame.Color(64, 64, 64, 255) + mc2 = mc1 + c2 + self.assertTrue(isinstance(mc2, self.MyColor)) + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") + c3 = c2 + mc1 + self.assertTrue(type(c3) is pygame.Color) + + def test_sub(self): + mc1 = self.MyColor(128, 128, 128, 255) + self.assertTrue(mc1.an_attribute) + c2 = pygame.Color(64, 64, 64, 255) + mc2 = mc1 - c2 + self.assertTrue(isinstance(mc2, self.MyColor)) + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") + c3 = c2 - mc1 + self.assertTrue(type(c3) is pygame.Color) + + def test_mul(self): + mc1 = self.MyColor(128, 128, 128, 255) + self.assertTrue(mc1.an_attribute) + c2 = pygame.Color(64, 64, 64, 255) + mc2 = mc1 * c2 + self.assertTrue(isinstance(mc2, self.MyColor)) + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") + c3 = c2 * mc1 + self.assertTrue(type(c3) is pygame.Color) + + def test_div(self): + mc1 = self.MyColor(128, 128, 128, 255) + self.assertTrue(mc1.an_attribute) + c2 = pygame.Color(64, 64, 64, 255) + mc2 = mc1 // c2 + self.assertTrue(isinstance(mc2, self.MyColor)) + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") + c3 = c2 // mc1 + self.assertTrue(type(c3) is pygame.Color) + + def test_mod(self): + mc1 = self.MyColor(128, 128, 128, 255) + self.assertTrue(mc1.an_attribute) + c2 = pygame.Color(64, 64, 64, 255) + mc2 = mc1 % c2 + self.assertTrue(isinstance(mc2, self.MyColor)) + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") + c3 = c2 % mc1 + self.assertTrue(type(c3) is pygame.Color) + + def test_inv(self): + mc1 = self.MyColor(64, 64, 64, 64) + self.assertTrue(mc1.an_attribute) + mc2 = ~mc1 + self.assertTrue(isinstance(mc2, self.MyColor)) + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") + + def test_correct_gamma(self): + mc1 = self.MyColor(64, 70, 75, 255) + self.assertTrue(mc1.an_attribute) + mc2 = mc1.correct_gamma(0.03) + self.assertTrue(isinstance(mc2, self.MyColor)) + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/compat_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/compat_test.py new file mode 100644 index 0000000..129f8bf --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/compat_test.py @@ -0,0 +1,89 @@ +import sys + +import unittest +from pygame import compat + +encode_file_path = sys.modules["pygame.rwobject"].encode_file_path + + +class CompatModuleTest(unittest.TestCase): + def test_as_unicode(self): + r = r"Bo\u00F6tes" + ords = [ord("B"), ord("o"), 0xF6, ord("t"), ord("e"), ord("s")] + self.assertEqual(len(r), 11) + u = compat.as_unicode(r) + self.assertIsInstance(u, compat.unicode_) + self.assertEqual([ord(c) for c in u], ords) + + def test_as_bytes(self): + ords = [0, 1, 0x7F, 0x80, 0xC3, 0x20, 0xC3, 0xB6, 0xFF] + s = "".join([chr(i) for i in ords]) + self.assertEqual(len(s), len(ords)) + b = compat.as_bytes(s) + self.assertIsInstance(b, compat.bytes_) + self.assertEqual([compat.ord_(i) for i in b], ords) + + def test_ord_(self): + self.assertIsInstance(compat.ord_(compat.bytes_(1)[0]), int) + + def test_bytes_(self): + self.assertFalse(compat.bytes_ is compat.unicode_) + self.assertTrue(hasattr(compat.bytes_, "capitalize")) + self.assertFalse(hasattr(compat.bytes_, "isdecimal")) + + def test_unicode_(self): + self.assertTrue(hasattr(compat.unicode_(), "isdecimal")) + + def test_long_(self): + self.assertIsInstance(int("99999999999999999999"), compat.long_) + + def test_geterror(self): + msg = "Success" + try: + raise TypeError(msg) + except TypeError: + e = compat.geterror() + self.assertIsInstance(e, TypeError) + self.assertEqual(str(e), msg) + + def test_xrange_(self): + self.assertFalse(isinstance(compat.xrange_(2), list)) + + def test_unichr_(self): + ordval = 86 + c = compat.unichr_(ordval) + self.assertIsInstance(c, compat.unicode_) + self.assertEqual(ord(c), ordval) + + def test_get_BytesIO(self): + BytesIO = compat.get_BytesIO() + b1 = compat.as_bytes("\x00\xffabc") + b2 = BytesIO(b1).read() + self.assertIsInstance(b2, compat.bytes_) + self.assertEqual(b2, b1) + + def test_get_StringIO(self): + StringIO = compat.get_StringIO() + b1 = "abcde" + b2 = StringIO(b1).read() + self.assertIsInstance(b2, str) + self.assertEqual(b2, b1) + + def test_raw_input_(self): + StringIO = compat.get_StringIO() + msg = "success" + tmp = sys.stdin + sys.stdin = StringIO(msg + "\n") + try: + s = compat.raw_input_() + self.assertEqual(s, msg) + finally: + sys.stdin = tmp + + def test_filesystem_encode(self): + upath = compat.as_unicode(r"ab\u212Acd") + self.assertEqual(compat.filesystem_encode(upath), encode_file_path(upath)) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/constants_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/constants_test.py new file mode 100644 index 0000000..0980dc3 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/constants_test.py @@ -0,0 +1,458 @@ +import unittest +import pygame.constants + + +SDL2 = pygame.get_sdl_version()[0] >= 2 + + +# K_* and KSCAN_* common names. +K_AND_KSCAN_COMMON_NAMES = ( + "UNKNOWN", + "BACKSPACE", + "TAB", + "CLEAR", + "RETURN", + "PAUSE", + "ESCAPE", + "SPACE", + "COMMA", + "MINUS", + "PERIOD", + "SLASH", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "SEMICOLON", + "EQUALS", + "LEFTBRACKET", + "BACKSLASH", + "RIGHTBRACKET", + "DELETE", + "KP0", + "KP1", + "KP2", + "KP3", + "KP4", + "KP5", + "KP6", + "KP7", + "KP8", + "KP9", + "KP_PERIOD", + "KP_DIVIDE", + "KP_MULTIPLY", + "KP_MINUS", + "KP_PLUS", + "KP_ENTER", + "KP_EQUALS", + "UP", + "DOWN", + "RIGHT", + "LEFT", + "INSERT", + "HOME", + "END", + "PAGEUP", + "PAGEDOWN", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12", + "F13", + "F14", + "F15", + "NUMLOCK", + "CAPSLOCK", + "SCROLLOCK", + "RSHIFT", + "LSHIFT", + "RCTRL", + "LCTRL", + "RALT", + "LALT", + "RMETA", + "LMETA", + "LSUPER", + "RSUPER", + "MODE", + "HELP", + "PRINT", + "SYSREQ", + "BREAK", + "MENU", + "POWER", + "EURO", +) + +if SDL2: + K_AND_KSCAN_COMMON_NAMES += ( + "KP_0", + "KP_1", + "KP_2", + "KP_3", + "KP_4", + "KP_5", + "KP_6", + "KP_7", + "KP_8", + "KP_9", + "NUMLOCKCLEAR", + "SCROLLLOCK", + "RGUI", + "LGUI", + "PRINTSCREEN", + "CURRENCYUNIT", + "CURRENCYSUBUNIT", + ) + + +# Constants that have the same value. +K_AND_KSCAN_COMMON_OVERLAPS = () + +if SDL2: + K_AND_KSCAN_COMMON_OVERLAPS += ( + ("KP0", "KP_0"), + ("KP1", "KP_1"), + ("KP2", "KP_2"), + ("KP3", "KP_3"), + ("KP4", "KP_4"), + ("KP5", "KP_5"), + ("KP6", "KP_6"), + ("KP7", "KP_7"), + ("KP8", "KP_8"), + ("KP9", "KP_9"), + ("NUMLOCK", "NUMLOCKCLEAR"), + ("SCROLLOCK", "SCROLLLOCK"), + ("LSUPER", "LMETA", "LGUI"), + ("RSUPER", "RMETA", "RGUI"), + ("PRINT", "PRINTSCREEN"), + ("BREAK", "PAUSE"), + ("EURO", "CURRENCYUNIT"), + ) + + +def create_overlap_set(constant_names): + """Helper function to find overlapping constant values/names. + + Returns a set of fronzensets: + set(frozenset(names of overlapping constants), ...) + """ + # Create an overlap dict. + overlap_dict = {} + + for name in constant_names: + value = getattr(pygame.constants, name) + overlap_dict.setdefault(value, set()).add(name) + + # Get all entries with more than 1 value. + overlaps = set() + + for overlap_names in overlap_dict.values(): + if len(overlap_names) > 1: + overlaps.add(frozenset(overlap_names)) + + return overlaps + + +class KConstantsTests(unittest.TestCase): + """Test K_* (key) constants.""" + + # K_* specific names. + K_SPECIFIC_NAMES = ( + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "QUOTE", + "BACKQUOTE", + "EXCLAIM", + "QUOTEDBL", + "HASH", + "DOLLAR", + "AMPERSAND", + "LEFTPAREN", + "RIGHTPAREN", + "ASTERISK", + "PLUS", + "COLON", + "LESS", + "GREATER", + "QUESTION", + "AT", + "CARET", + "UNDERSCORE", + ) + + if SDL2: + K_SPECIFIC_NAMES += ("PERCENT",) + + # Create a sequence of all the K_* constant names. + K_NAMES = tuple("K_" + n for n in K_AND_KSCAN_COMMON_NAMES + K_SPECIFIC_NAMES) + + def test_k__existence(self): + """Ensures K constants exist.""" + for name in self.K_NAMES: + self.assertTrue( + hasattr(pygame.constants, name), "missing constant {}".format(name) + ) + + def test_k__type(self): + """Ensures K constants are the correct type.""" + for name in self.K_NAMES: + value = getattr(pygame.constants, name) + + self.assertIs(type(value), int) + + def test_k__value_overlap(self): + """Ensures no unexpected K constant values overlap.""" + EXPECTED_OVERLAPS = set( + [ + frozenset(["K_" + n for n in item]) + for item in K_AND_KSCAN_COMMON_OVERLAPS + ] + ) + + overlaps = create_overlap_set(self.K_NAMES) + + self.assertSetEqual(overlaps, EXPECTED_OVERLAPS) + + +@unittest.skipIf(not SDL2, "requires SDL2") +class KscanConstantsTests(unittest.TestCase): + """Test KSCAN_* (scancode) constants.""" + + # KSCAN_* specific names. + KSCAN_SPECIFIC_NAMES = ( + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "APOSTROPHE", + "GRAVE", + "INTERNATIONAL1", + "INTERNATIONAL2", + "INTERNATIONAL3", + "INTERNATIONAL4", + "INTERNATIONAL5", + "INTERNATIONAL6", + "INTERNATIONAL7", + "INTERNATIONAL8", + "INTERNATIONAL9", + "LANG1", + "LANG2", + "LANG3", + "LANG4", + "LANG5", + "LANG6", + "LANG7", + "LANG8", + "LANG9", + "NONUSBACKSLASH", + "NONUSHASH", + ) + + # Create a sequence of all the KSCAN_* constant names. + KSCAN_NAMES = tuple( + "KSCAN_" + n for n in K_AND_KSCAN_COMMON_NAMES + KSCAN_SPECIFIC_NAMES + ) + + def test_kscan__existence(self): + """Ensures KSCAN constants exist.""" + for name in self.KSCAN_NAMES: + self.assertTrue( + hasattr(pygame.constants, name), "missing constant {}".format(name) + ) + + def test_kscan__type(self): + """Ensures KSCAN constants are the correct type.""" + for name in self.KSCAN_NAMES: + value = getattr(pygame.constants, name) + + self.assertIs(type(value), int) + + def test_kscan__value_overlap(self): + """Ensures no unexpected KSCAN constant values overlap.""" + EXPECTED_OVERLAPS = set( + [ + frozenset(["KSCAN_" + n for n in item]) + for item in K_AND_KSCAN_COMMON_OVERLAPS + ] + ) + + overlaps = create_overlap_set(self.KSCAN_NAMES) + + self.assertSetEqual(overlaps, EXPECTED_OVERLAPS) + + +class KmodConstantsTests(unittest.TestCase): + """Test KMOD_* (key modifier) constants.""" + + # KMOD_* constant names. + KMOD_CONSTANTS = ( + "KMOD_NONE", + "KMOD_LSHIFT", + "KMOD_RSHIFT", + "KMOD_SHIFT", + "KMOD_LCTRL", + "KMOD_RCTRL", + "KMOD_CTRL", + "KMOD_LALT", + "KMOD_RALT", + "KMOD_ALT", + "KMOD_LMETA", + "KMOD_RMETA", + "KMOD_META", + "KMOD_NUM", + "KMOD_CAPS", + "KMOD_MODE", + ) + + if SDL2: + KMOD_CONSTANTS += ("KMOD_LGUI", "KMOD_RGUI", "KMOD_GUI") + + def test_kmod__existence(self): + """Ensures KMOD constants exist.""" + for name in self.KMOD_CONSTANTS: + self.assertTrue( + hasattr(pygame.constants, name), "missing constant {}".format(name) + ) + + def test_kmod__type(self): + """Ensures KMOD constants are the correct type.""" + for name in self.KMOD_CONSTANTS: + value = getattr(pygame.constants, name) + + self.assertIs(type(value), int) + + def test_kmod__value_overlap(self): + """Ensures no unexpected KMOD constant values overlap.""" + # KMODs that have the same values. + EXPECTED_OVERLAPS = set() + + if SDL2: + EXPECTED_OVERLAPS.update( + [ + frozenset(["KMOD_LGUI", "KMOD_LMETA"]), + frozenset(["KMOD_RGUI", "KMOD_RMETA"]), + frozenset(["KMOD_GUI", "KMOD_META"]), + ] + ) + + overlaps = create_overlap_set(self.KMOD_CONSTANTS) + + self.assertSetEqual(overlaps, EXPECTED_OVERLAPS) + + def test_kmod__no_bitwise_overlap(self): + """Ensures certain KMOD constants have no overlapping bits.""" + NO_BITWISE_OVERLAP = ( + "KMOD_NONE", + "KMOD_LSHIFT", + "KMOD_RSHIFT", + "KMOD_LCTRL", + "KMOD_RCTRL", + "KMOD_LALT", + "KMOD_RALT", + "KMOD_LMETA", + "KMOD_RMETA", + "KMOD_NUM", + "KMOD_CAPS", + "KMOD_MODE", + ) + + kmods = 0 + + for name in NO_BITWISE_OVERLAP: + value = getattr(pygame.constants, name) + + self.assertFalse(kmods & value) + + kmods |= value + + def test_kmod__bitwise_overlap(self): + """Ensures certain KMOD constants have overlapping bits.""" + # KMODS that are comprised of other KMODs. + KMOD_COMPRISED_DICT = { + "KMOD_SHIFT": ("KMOD_LSHIFT", "KMOD_RSHIFT"), + "KMOD_CTRL": ("KMOD_LCTRL", "KMOD_RCTRL"), + "KMOD_ALT": ("KMOD_LALT", "KMOD_RALT"), + "KMOD_META": ("KMOD_LMETA", "KMOD_RMETA"), + } + + if SDL2: + KMOD_COMPRISED_DICT.update({"KMOD_GUI": ("KMOD_LGUI", "KMOD_RGUI")}) + + for base_name, seq_names in KMOD_COMPRISED_DICT.items(): + expected_value = 0 # Reset. + + for name in seq_names: + expected_value |= getattr(pygame.constants, name) + + value = getattr(pygame.constants, base_name) + + self.assertEqual(value, expected_value) + + +################################################################################ + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/cursors_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/cursors_test.py new file mode 100644 index 0000000..b77f8bc --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/cursors_test.py @@ -0,0 +1,64 @@ +import unittest +from pygame.tests.test_utils import fixture_path +import pygame + + +class CursorsModuleTest(unittest.TestCase): + def todo_test_compile(self): + + # __doc__ (as of 2008-06-25) for pygame.cursors.compile: + + # pygame.cursors.compile(strings, black, white,xor) -> data, mask + # compile cursor strings into cursor data + # + # This takes a set of strings with equal length and computes + # the binary data for that cursor. The string widths must be + # divisible by 8. + # + # The black and white arguments are single letter strings that + # tells which characters will represent black pixels, and which + # characters represent white pixels. All other characters are + # considered clear. + # + # This returns a tuple containing the cursor data and cursor mask + # data. Both these arguments are used when setting a cursor with + # pygame.mouse.set_cursor(). + + self.fail() + + def test_load_xbm(self): + # __doc__ (as of 2008-06-25) for pygame.cursors.load_xbm: + + # pygame.cursors.load_xbm(cursorfile, maskfile) -> cursor_args + # reads a pair of XBM files into set_cursor arguments + # + # Arguments can either be filenames or filelike objects + # with the readlines method. Not largely tested, but + # should work with typical XBM files. + + # Test that load_xbm will take filenames as arguments + cursorfile = fixture_path(r"xbm_cursors/white_sizing.xbm") + maskfile = fixture_path(r"xbm_cursors/white_sizing_mask.xbm") + cursor = pygame.cursors.load_xbm(cursorfile, maskfile) + + # Test that load_xbm will take file objects as arguments + with open(cursorfile) as cursor_f, open(maskfile) as mask_f: + cursor = pygame.cursors.load_xbm(cursor_f, mask_f) + + # Is it in a format that mouse.set_cursor won't blow up on? + pygame.display.init() + try: + pygame.mouse.set_cursor(*cursor) + except pygame.error as e: + if "not currently supported" in str(e): + unittest.skip("skipping test as set_cursor() is not supported") + finally: + pygame.display.quit() + + +################################################################################ + +if __name__ == "__main__": + unittest.main() + +################################################################################ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/display_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/display_test.py new file mode 100644 index 0000000..a15c6fd --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/display_test.py @@ -0,0 +1,433 @@ +# -*- coding: utf-8 -*- + +import unittest +import os + +import pygame, pygame.transform +from pygame.compat import unicode_ + +from pygame import display + + +SDL2 = pygame.get_sdl_version()[0] >= 2 + + +class DisplayModuleTest(unittest.TestCase): + default_caption = "pygame window" + + def setUp(self): + display.init() + + def tearDown(self): + display.quit() + + def test_update(self): + """ see if pygame.display.update takes rects with negative values. + "|Tags:display|" + """ + screen = pygame.display.set_mode((100, 100)) + screen.fill((55, 55, 55)) + + r1 = pygame.Rect(0, 0, 100, 100) + pygame.display.update(r1) + + r2 = pygame.Rect(-10, 0, 100, 100) + pygame.display.update(r2) + + r3 = pygame.Rect(-10, 0, -100, -100) + pygame.display.update(r3) + + def test_Info(self): + inf = pygame.display.Info() + self.assertNotEqual(inf.current_h, -1) + self.assertNotEqual(inf.current_w, -1) + # probably have an older SDL than 1.2.10 if -1. + + screen = pygame.display.set_mode((128, 128)) + inf = pygame.display.Info() + self.assertEqual(inf.current_h, 128) + self.assertEqual(inf.current_w, 128) + + def todo_test_flip(self): + + # __doc__ (as of 2008-08-02) for pygame.display.flip: + + # pygame.display.flip(): return None + # update the full display Surface to the screen + # + # This will update the contents of the entire display. If your display + # mode is using the flags pygame.HWSURFACE and pygame.DOUBLEBUF, this + # will wait for a vertical retrace and swap the surfaces. If you are + # using a different type of display mode, it will simply update the + # entire contents of the surface. + # + # When using an pygame.OPENGL display mode this will perform a gl buffer swap. + + self.fail() + + def todo_test_get_active(self): + + # __doc__ (as of 2008-08-02) for pygame.display.get_active: + + # pygame.display.get_active(): return bool + # true when the display is active on the display + # + # After pygame.display.set_mode() is called the display Surface will + # be visible on the screen. Most windowed displays can be hidden by + # the user. If the display Surface is hidden or iconified this will + # return False. + # + + self.fail() + + def test_get_caption(self): + screen = display.set_mode((100, 100)) + + self.assertEqual(display.get_caption()[0], self.default_caption) + + def test_set_caption(self): + TEST_CAPTION = "test" + screen = display.set_mode((100, 100)) + + self.assertIsNone(display.set_caption(TEST_CAPTION)) + self.assertEqual(display.get_caption()[0], TEST_CAPTION) + self.assertEqual(display.get_caption()[1], TEST_CAPTION) + + def test_caption_unicode(self): + TEST_CAPTION = u"台" + display.set_caption(TEST_CAPTION) + import sys + + if sys.version_info.major >= 3: + self.assertEqual(display.get_caption()[0], TEST_CAPTION) + else: + self.assertEqual(unicode_(display.get_caption()[0], "utf8"), TEST_CAPTION) + + def todo_test_get_driver(self): + + # __doc__ (as of 2008-08-02) for pygame.display.get_driver: + + # pygame.display.get_driver(): return name + # get the name of the pygame display backend + # + # Pygame chooses one of many available display backends when it is + # initialized. This returns the internal name used for the display + # backend. This can be used to provide limited information about what + # display capabilities might be accelerated. See the SDL_VIDEODRIVER + # flags in pygame.display.set_mode() to see some of the common + # options. + # + + self.fail() + + def test_get_init(self): + """Ensures the module's initialization state can be retrieved.""" + # display.init() already called in setUp() + self.assertTrue(display.get_init()) + + # This decorator can be removed (or test changed) when issues #991 and #993 + # are resolved. + @unittest.skipIf(SDL2, "SDL2 issues") + def test_get_surface(self): + """Ensures get_surface gets the current display surface.""" + lengths = (1, 5, 100) + + for expected_size in ((w, h) for w in lengths for h in lengths): + for expected_depth in (8, 16, 24, 32): + expected_surface = display.set_mode(expected_size, 0, expected_depth) + + surface = pygame.display.get_surface() + + self.assertEqual(surface, expected_surface) + self.assertIsInstance(surface, pygame.Surface) + self.assertEqual(surface.get_size(), expected_size) + self.assertEqual(surface.get_bitsize(), expected_depth) + + def test_get_surface__mode_not_set(self): + """Ensures get_surface handles the display mode not being set.""" + surface = pygame.display.get_surface() + + self.assertIsNone(surface) + + def todo_test_get_wm_info(self): + + # __doc__ (as of 2008-08-02) for pygame.display.get_wm_info: + + # pygame.display.get_wm_info(): return dict + # Get information about the current windowing system + # + # Creates a dictionary filled with string keys. The strings and values + # are arbitrarily created by the system. Some systems may have no + # information and an empty dictionary will be returned. Most platforms + # will return a "window" key with the value set to the system id for + # the current display. + # + # New with pygame 1.7.1 + + self.fail() + + def todo_test_gl_get_attribute(self): + + # __doc__ (as of 2008-08-02) for pygame.display.gl_get_attribute: + + # pygame.display.gl_get_attribute(flag): return value + # get the value for an opengl flag for the current display + # + # After calling pygame.display.set_mode() with the pygame.OPENGL flag, + # it is a good idea to check the value of any requested OpenGL + # attributes. See pygame.display.gl_set_attribute() for a list of + # valid flags. + # + + self.fail() + + def todo_test_gl_set_attribute(self): + + # __doc__ (as of 2008-08-02) for pygame.display.gl_set_attribute: + + # pygame.display.gl_set_attribute(flag, value): return None + # request an opengl display attribute for the display mode + # + # When calling pygame.display.set_mode() with the pygame.OPENGL flag, + # Pygame automatically handles setting the OpenGL attributes like + # color and doublebuffering. OpenGL offers several other attributes + # you may want control over. Pass one of these attributes as the flag, + # and its appropriate value. This must be called before + # pygame.display.set_mode() + # + # The OPENGL flags are; + # GL_ALPHA_SIZE, GL_DEPTH_SIZE, GL_STENCIL_SIZE, GL_ACCUM_RED_SIZE, + # GL_ACCUM_GREEN_SIZE, GL_ACCUM_BLUE_SIZE, GL_ACCUM_ALPHA_SIZE, + # GL_MULTISAMPLEBUFFERS, GL_MULTISAMPLESAMPLES, GL_STEREO + + self.fail() + + def todo_test_iconify(self): + + # __doc__ (as of 2008-08-02) for pygame.display.iconify: + + # pygame.display.iconify(): return bool + # iconify the display surface + # + # Request the window for the display surface be iconified or hidden. + # Not all systems and displays support an iconified display. The + # function will return True if successfull. + # + # When the display is iconified pygame.display.get_active() will + # return False. The event queue should receive a ACTIVEEVENT event + # when the window has been iconified. + # + + self.fail() + + def test_init(self): + """Ensures the module is initialized after init called.""" + # display.init() already called in setUp(), so quit and re-init + display.quit() + display.init() + + self.assertTrue(display.get_init()) + + def test_init__multiple(self): + """Ensures the module is initialized after multiple init calls.""" + display.init() + display.init() + + self.assertTrue(display.get_init()) + + def test_list_modes(self): + modes = pygame.display.list_modes(depth=0, flags=pygame.FULLSCREEN, display=0) + # modes == -1 means any mode is supported. + if modes != -1: + self.assertEqual(len(modes[0]), 2) + self.assertEqual(type(modes[0][0]), int) + + modes = pygame.display.list_modes() + if modes != -1: + self.assertEqual(len(modes[0]), 2) + self.assertEqual(type(modes[0][0]), int) + + modes = pygame.display.list_modes(depth=0, flags=0, display=0) + if modes != -1: + self.assertEqual(len(modes[0]), 2) + self.assertEqual(type(modes[0][0]), int) + + def test_mode_ok(self): + pygame.display.mode_ok((128, 128)) + modes = pygame.display.list_modes() + if modes != -1: + size = modes[0] + self.assertNotEqual(pygame.display.mode_ok(size), 0) + + pygame.display.mode_ok((128, 128), 0, 32) + pygame.display.mode_ok((128, 128), flags=0, depth=32, display=0) + + def test_mode_ok_fullscreen(self): + modes = pygame.display.list_modes() + if modes != -1: + size = modes[0] + self.assertNotEqual( + pygame.display.mode_ok(size, flags=pygame.FULLSCREEN), 0 + ) + + def test_mode_ok_scaled(self): + modes = pygame.display.list_modes() + if modes != -1: + size = modes[0] + self.assertNotEqual(pygame.display.mode_ok(size, flags=pygame.SCALED), 0) + + def test_get_num_displays(self): + self.assertGreater(pygame.display.get_num_displays(), 0) + + def test_quit(self): + """Ensures the module is not initialized after quit called.""" + display.quit() + + self.assertFalse(display.get_init()) + + def test_quit__multiple(self): + """Ensures the module is not initialized after multiple quit calls.""" + display.quit() + display.quit() + + self.assertFalse(display.get_init()) + + def todo_test_set_gamma(self): + + # __doc__ (as of 2008-08-02) for pygame.display.set_gamma: + + # pygame.display.set_gamma(red, green=None, blue=None): return bool + # change the hardware gamma ramps + # + # Set the red, green, and blue gamma values on the display hardware. + # If the green and blue arguments are not passed, they will both be + # the same as red. Not all systems and hardware support gamma ramps, + # if the function succeeds it will return True. + # + # A gamma value of 1.0 creates a linear color table. Lower values will + # darken the display and higher values will brighten. + # + + self.fail() + + def todo_test_set_gamma_ramp(self): + + # __doc__ (as of 2008-08-02) for pygame.display.set_gamma_ramp: + + # change the hardware gamma ramps with a custom lookup + # pygame.display.set_gamma_ramp(red, green, blue): return bool + # set_gamma_ramp(red, green, blue): return bool + # + # Set the red, green, and blue gamma ramps with an explicit lookup + # table. Each argument should be sequence of 256 integers. The + # integers should range between 0 and 0xffff. Not all systems and + # hardware support gamma ramps, if the function succeeds it will + # return True. + # + + self.fail() + + def todo_test_set_icon(self): + + # __doc__ (as of 2008-08-02) for pygame.display.set_icon: + + # pygame.display.set_icon(Surface): return None + # change the system image for the display window + # + # Sets the runtime icon the system will use to represent the display + # window. All windows default to a simple pygame logo for the window + # icon. + # + # You can pass any surface, but most systems want a smaller image + # around 32x32. The image can have colorkey transparency which will be + # passed to the system. + # + # Some systems do not allow the window icon to change after it has + # been shown. This function can be called before + # pygame.display.set_mode() to create the icon before the display mode + # is set. + # + + self.fail() + + def test_set_mode_kwargs(self): + + pygame.display.set_mode(size=(1, 1), flags=0, depth=0, display=0) + + def test_set_mode_scaled(self): + surf = pygame.display.set_mode( + size=(1, 1), flags=pygame.SCALED, depth=0, display=0 + ) + winsize = pygame.display.get_window_size() + self.assertEqual( + winsize[0] % surf.get_size()[0], + 0, + "window width should be a multiple of the surface width", + ) + self.assertEqual( + winsize[1] % surf.get_size()[1], + 0, + "window height should be a multiple of the surface height", + ) + self.assertEqual( + winsize[0] / surf.get_size()[0], winsize[1] / surf.get_size()[1] + ) + + def todo_test_set_palette(self): + + # __doc__ (as of 2008-08-02) for pygame.display.set_palette: + + # pygame.display.set_palette(palette=None): return None + # set the display color palette for indexed displays + # + # This will change the video display color palette for 8bit displays. + # This does not change the palette for the actual display Surface, + # only the palette that is used to display the Surface. If no palette + # argument is passed, the system default palette will be restored. The + # palette is a sequence of RGB triplets. + # + + self.fail() + + def todo_test_toggle_fullscreen(self): + + # __doc__ (as of 2008-08-02) for pygame.display.toggle_fullscreen: + + # pygame.display.toggle_fullscreen(): return bool + # switch between fullscreen and windowed displays + # + # Switches the display window between windowed and fullscreen modes. + # This function only works under the unix x11 video driver. For most + # situations it is better to call pygame.display.set_mode() with new + # display flags. + # + + self.fail() + + +@unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", + 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER', +) +class DisplayOpenGLTest(unittest.TestCase): + def test_screen_size_opengl(self): + """ returns a surface with the same size requested. + |tags:display,slow,opengl| + """ + pygame.display.init() + screen = pygame.display.set_mode((640, 480), pygame.OPENGL) + self.assertEqual((640, 480), screen.get_size()) + + +class X11CrashTest(unittest.TestCase): + def test_x11_set_mode_crash_gh1654(self): + # Test for https://github.com/pygame/pygame/issues/1654 + # If unfixed, this will trip a segmentation fault + pygame.display.init() + pygame.display.quit() + screen = pygame.display.set_mode((640, 480), 0) + self.assertEqual((640, 480), screen.get_size()) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/draw_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/draw_test.py new file mode 100644 index 0000000..37c6e93 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/draw_test.py @@ -0,0 +1,6362 @@ +import math +import unittest +import sys + +import pygame +from pygame import draw +from pygame import draw_py +from pygame.locals import SRCALPHA +from pygame.tests import test_utils +from pygame.math import Vector2 + +PY3 = sys.version_info >= (3, 0, 0) + +RED = BG_RED = pygame.Color("red") +GREEN = FG_GREEN = pygame.Color("green") + +# Clockwise from the top left corner and ending with the center point. +RECT_POSITION_ATTRIBUTES = ( + "topleft", + "midtop", + "topright", + "midright", + "bottomright", + "midbottom", + "bottomleft", + "midleft", + "center", +) + + +def get_border_values(surface, width, height): + """Returns a list containing lists with the values of the surface's + borders. + """ + border_top = [surface.get_at((x, 0)) for x in range(width)] + border_left = [surface.get_at((0, y)) for y in range(height)] + border_right = [surface.get_at((width - 1, y)) for y in range(height)] + border_bottom = [surface.get_at((x, height - 1)) for x in range(width)] + + return [border_top, border_left, border_right, border_bottom] + + +def corners(surface): + """Returns a tuple with the corner positions of the given surface. + + Clockwise from the top left corner. + """ + width, height = surface.get_size() + return ((0, 0), (width - 1, 0), (width - 1, height - 1), (0, height - 1)) + + +def rect_corners_mids_and_center(rect): + """Returns a tuple with each corner, mid, and the center for a given rect. + + Clockwise from the top left corner and ending with the center point. + """ + return ( + rect.topleft, + rect.midtop, + rect.topright, + rect.midright, + rect.bottomright, + rect.midbottom, + rect.bottomleft, + rect.midleft, + rect.center, + ) + + +def border_pos_and_color(surface): + """Yields each border position and its color for a given surface. + + Clockwise from the top left corner. + """ + width, height = surface.get_size() + right, bottom = width - 1, height - 1 + + # Top edge. + for x in range(width): + pos = (x, 0) + yield pos, surface.get_at(pos) + + # Right edge. + # Top right done in top edge loop. + for y in range(1, height): + pos = (right, y) + yield pos, surface.get_at(pos) + + # Bottom edge. + # Bottom right done in right edge loop. + for x in range(right - 1, -1, -1): + pos = (x, bottom) + yield pos, surface.get_at(pos) + + # Left edge. + # Bottom left done in bottom edge loop. Top left done in top edge loop. + for y in range(bottom - 1, 0, -1): + pos = (0, y) + yield pos, surface.get_at(pos) + + +def get_color_points(surface, color, bounds_rect=None, match_color=True): + """Get all the points of a given color on the surface within the given + bounds. + + If bounds_rect is None the full surface is checked. + If match_color is True, all points matching the color are returned, + otherwise all points not matching the color are returned. + """ + get_at = surface.get_at # For possible speed up. + + if bounds_rect is None: + x_range = range(surface.get_width()) + y_range = range(surface.get_height()) + else: + x_range = range(bounds_rect.left, bounds_rect.right) + y_range = range(bounds_rect.top, bounds_rect.bottom) + + surface.lock() # For possible speed up. + + if match_color: + pts = [(x, y) for x in x_range for y in y_range if get_at((x, y)) == color] + else: + pts = [(x, y) for x in x_range for y in y_range if get_at((x, y)) != color] + + surface.unlock() + return pts + + +def create_bounding_rect(surface, surf_color, default_pos): + """Create a rect to bound all the pixels that don't match surf_color. + + The default_pos parameter is used to position the bounding rect for the + case where all pixels match the surf_color. + """ + width, height = surface.get_clip().size + xmin, ymin = width, height + xmax, ymax = -1, -1 + get_at = surface.get_at # For possible speed up. + + surface.lock() # For possible speed up. + + for y in range(height): + for x in range(width): + if get_at((x, y)) != surf_color: + xmin = min(x, xmin) + xmax = max(x, xmax) + ymin = min(y, ymin) + ymax = max(y, ymax) + + surface.unlock() + + if -1 == xmax: + # No points means a 0 sized rect positioned at default_pos. + return pygame.Rect(default_pos, (0, 0)) + return pygame.Rect((xmin, ymin), (xmax - xmin + 1, ymax - ymin + 1)) + + +class InvalidBool(object): + """To help test invalid bool values.""" + + __nonzero__ = None + __bool__ = None + + +class DrawTestCase(unittest.TestCase): + """Base class to test draw module functions.""" + + draw_rect = staticmethod(draw.rect) + draw_polygon = staticmethod(draw.polygon) + draw_circle = staticmethod(draw.circle) + draw_ellipse = staticmethod(draw.ellipse) + draw_arc = staticmethod(draw.arc) + draw_line = staticmethod(draw.line) + draw_lines = staticmethod(draw.lines) + draw_aaline = staticmethod(draw.aaline) + draw_aalines = staticmethod(draw.aalines) + + +class PythonDrawTestCase(unittest.TestCase): + """Base class to test draw_py module functions.""" + + # draw_py is currently missing some functions. + # draw_rect = staticmethod(draw_py.draw_rect) + draw_polygon = staticmethod(draw_py.draw_polygon) + # draw_circle = staticmethod(draw_py.draw_circle) + # draw_ellipse = staticmethod(draw_py.draw_ellipse) + # draw_arc = staticmethod(draw_py.draw_arc) + draw_line = staticmethod(draw_py.draw_line) + draw_lines = staticmethod(draw_py.draw_lines) + draw_aaline = staticmethod(draw_py.draw_aaline) + draw_aalines = staticmethod(draw_py.draw_aalines) + + +### Ellipse Testing ########################################################### + + +class DrawEllipseMixin(object): + """Mixin tests for drawing ellipses. + + This class contains all the general ellipse drawing tests. + """ + + def test_ellipse__args(self): + """Ensures draw ellipse accepts the correct args.""" + bounds_rect = self.draw_ellipse( + pygame.Surface((3, 3)), (0, 10, 0, 50), pygame.Rect((0, 0), (3, 2)), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__args_without_width(self): + """Ensures draw ellipse accepts the args without a width.""" + bounds_rect = self.draw_ellipse( + pygame.Surface((2, 2)), (1, 1, 1, 99), pygame.Rect((1, 1), (1, 1)) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__args_with_negative_width(self): + """Ensures draw ellipse accepts the args with negative width.""" + bounds_rect = self.draw_ellipse( + pygame.Surface((3, 3)), (0, 10, 0, 50), pygame.Rect((2, 3), (3, 2)), -1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + self.assertEqual(bounds_rect, pygame.Rect(2, 3, 0, 0)) + + def test_ellipse__args_with_width_gt_radius(self): + """Ensures draw ellipse accepts the args with + width > rect.w // 2 and width > rect.h // 2. + """ + rect = pygame.Rect((0, 0), (4, 4)) + bounds_rect = self.draw_ellipse( + pygame.Surface((3, 3)), (0, 10, 0, 50), rect, rect.w // 2 + 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + bounds_rect = self.draw_ellipse( + pygame.Surface((3, 3)), (0, 10, 0, 50), rect, rect.h // 2 + 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__kwargs(self): + """Ensures draw ellipse accepts the correct kwargs + with and without a width arg. + """ + kwargs_list = [ + { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("yellow"), + "rect": pygame.Rect((0, 0), (3, 2)), + "width": 1, + }, + { + "surface": pygame.Surface((2, 1)), + "color": (0, 10, 20), + "rect": (0, 0, 1, 1), + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_ellipse(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__kwargs_order_independent(self): + """Ensures draw ellipse's kwargs are not order dependent.""" + bounds_rect = self.draw_ellipse( + color=(1, 2, 3), + surface=pygame.Surface((3, 2)), + width=0, + rect=pygame.Rect((1, 0), (1, 1)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__args_missing(self): + """Ensures draw ellipse detects any missing required args.""" + surface = pygame.Surface((1, 1)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(surface, pygame.Color("red")) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse() + + def test_ellipse__kwargs_missing(self): + """Ensures draw ellipse detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((1, 2)), + "color": pygame.Color("red"), + "rect": pygame.Rect((1, 0), (2, 2)), + "width": 2, + } + + for name in ("rect", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(**invalid_kwargs) + + def test_ellipse__arg_invalid_types(self): + """Ensures draw ellipse detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + rect = pygame.Rect((1, 1), (1, 1)) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_ellipse(surface, color, rect, "1") + + with self.assertRaises(TypeError): + # Invalid rect. + bounds_rect = self.draw_ellipse(surface, color, (1, 2, 3, 4, 5), 1) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_ellipse(surface, 2.3, rect, 0) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_ellipse(rect, color, rect, 2) + + def test_ellipse__kwarg_invalid_types(self): + """Ensures draw ellipse detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + rect = pygame.Rect((0, 1), (1, 1)) + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "rect": rect, + "width": 1, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "rect": rect, + "width": 1, + }, + { + "surface": surface, + "color": color, + "rect": (0, 0, 0), # Invalid rect. + "width": 1, + }, + {"surface": surface, "color": color, "rect": rect, "width": 1.1}, + ] # Invalid width. + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(**kwargs) + + def test_ellipse__kwarg_invalid_name(self): + """Ensures draw ellipse detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + rect = pygame.Rect((0, 1), (2, 2)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "invalid": 1, + }, + {"surface": surface, "color": color, "rect": rect, "invalid": 1}, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(**kwargs) + + def test_ellipse__args_and_kwargs(self): + """Ensures draw ellipse accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 1)) + color = (255, 255, 0, 0) + rect = pygame.Rect((1, 0), (2, 1)) + width = 0 + kwargs = {"surface": surface, "color": color, "rect": rect, "width": width} + + for name in ("surface", "color", "rect", "width"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_ellipse(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_ellipse(surface, color, **kwargs) + elif "rect" == name: + bounds_rect = self.draw_ellipse(surface, color, rect, **kwargs) + else: + bounds_rect = self.draw_ellipse(surface, color, rect, width, **kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__valid_width_values(self): + """Ensures draw ellipse accepts different width values.""" + pos = (1, 1) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + color = (10, 20, 30, 255) + kwargs = { + "surface": surface, + "color": color, + "rect": pygame.Rect(pos, (3, 2)), + "width": None, + } + + for width in (-1000, -10, -1, 0, 1, 10, 1000): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = color if width >= 0 else surface_color + + bounds_rect = self.draw_ellipse(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__valid_rect_formats(self): + """Ensures draw ellipse accepts different rect formats.""" + pos = (1, 1) + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = {"surface": surface, "color": expected_color, "rect": None, "width": 0} + rects = (pygame.Rect(pos, (1, 3)), (pos, (2, 1)), (pos[0], pos[1], 1, 1)) + + for rect in rects: + surface.fill(surface_color) # Clear for each test. + kwargs["rect"] = rect + + bounds_rect = self.draw_ellipse(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__valid_color_formats(self): + """Ensures draw ellipse accepts different color formats.""" + pos = (1, 1) + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": None, + "rect": pygame.Rect(pos, (1, 2)), + "width": 0, + } + reds = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in reds: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_ellipse(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__invalid_color_formats(self): + """Ensures draw ellipse handles invalid color formats correctly.""" + pos = (1, 1) + surface = pygame.Surface((4, 3)) + kwargs = { + "surface": surface, + "color": None, + "rect": pygame.Rect(pos, (2, 2)), + "width": 1, + } + + for expected_color in (2.3, surface): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(**kwargs) + + def test_ellipse(self): + """Tests ellipses of differing sizes on surfaces of differing sizes. + + Checks if the number of sides touching the border of the surface is + correct. + """ + left_top = [(0, 0), (1, 0), (0, 1), (1, 1)] + sizes = [(4, 4), (5, 4), (4, 5), (5, 5)] + color = (1, 13, 24, 255) + + def same_size(width, height, border_width): + """Test for ellipses with the same size as the surface.""" + surface = pygame.Surface((width, height)) + + self.draw_ellipse(surface, color, (0, 0, width, height), border_width) + + # For each of the four borders check if it contains the color + borders = get_border_values(surface, width, height) + for border in borders: + self.assertTrue(color in border) + + def not_same_size(width, height, border_width, left, top): + """Test for ellipses that aren't the same size as the surface.""" + surface = pygame.Surface((width, height)) + + self.draw_ellipse( + surface, color, (left, top, width - 1, height - 1), border_width + ) + + borders = get_border_values(surface, width, height) + + # Check if two sides of the ellipse are touching the border + sides_touching = [color in border for border in borders].count(True) + self.assertEqual(sides_touching, 2) + + for width, height in sizes: + for border_width in (0, 1): + same_size(width, height, border_width) + for left, top in left_top: + not_same_size(width, height, border_width, left, top) + + def test_ellipse__thick_line(self): + """Ensures a thick lined ellipse is drawn correctly.""" + ellipse_color = pygame.Color("yellow") + surface_color = pygame.Color("black") + surface = pygame.Surface((40, 40)) + rect = pygame.Rect((0, 0), (31, 23)) + rect.center = surface.get_rect().center + + # As the lines get thicker the internals of the ellipse are not + # cleanly defined. So only test up to a few thicknesses before the + # maximum thickness. + for thickness in range(1, min(*rect.size) // 2 - 2): + surface.fill(surface_color) # Clear for each test. + + self.draw_ellipse(surface, ellipse_color, rect, thickness) + + surface.lock() # For possible speed up. + + # Check vertical thickness on the ellipse's top. + x = rect.centerx + y_start = rect.top + y_end = rect.top + thickness - 1 + + for y in range(y_start, y_end + 1): + self.assertEqual(surface.get_at((x, y)), ellipse_color, thickness) + + # Check pixels above and below this line. + self.assertEqual(surface.get_at((x, y_start - 1)), surface_color, thickness) + self.assertEqual(surface.get_at((x, y_end + 1)), surface_color, thickness) + + # Check vertical thickness on the ellipse's bottom. + x = rect.centerx + y_start = rect.bottom - thickness + y_end = rect.bottom - 1 + + for y in range(y_start, y_end + 1): + self.assertEqual(surface.get_at((x, y)), ellipse_color, thickness) + + # Check pixels above and below this line. + self.assertEqual(surface.get_at((x, y_start - 1)), surface_color, thickness) + self.assertEqual(surface.get_at((x, y_end + 1)), surface_color, thickness) + + # Check horizontal thickness on the ellipse's left. + x_start = rect.left + x_end = rect.left + thickness - 1 + y = rect.centery + + for x in range(x_start, x_end + 1): + self.assertEqual(surface.get_at((x, y)), ellipse_color, thickness) + + # Check pixels to the left and right of this line. + self.assertEqual(surface.get_at((x_start - 1, y)), surface_color, thickness) + self.assertEqual(surface.get_at((x_end + 1, y)), surface_color, thickness) + + # Check horizontal thickness on the ellipse's right. + x_start = rect.right - thickness + x_end = rect.right - 1 + y = rect.centery + + for x in range(x_start, x_end + 1): + self.assertEqual(surface.get_at((x, y)), ellipse_color, thickness) + + # Check pixels to the left and right of this line. + self.assertEqual(surface.get_at((x_start - 1, y)), surface_color, thickness) + self.assertEqual(surface.get_at((x_end + 1, y)), surface_color, thickness) + + surface.unlock() + + def test_ellipse__max_width(self): + """Ensures an ellipse with max width (and greater) is drawn correctly.""" + ellipse_color = pygame.Color("yellow") + surface_color = pygame.Color("black") + surface = pygame.Surface((40, 40)) + rect = pygame.Rect((0, 0), (31, 21)) + rect.center = surface.get_rect().center + max_thickness = (min(*rect.size) + 1) // 2 + + for thickness in range(max_thickness, max_thickness + 3): + surface.fill(surface_color) # Clear for each test. + + self.draw_ellipse(surface, ellipse_color, rect, thickness) + + surface.lock() # For possible speed up. + + # Check vertical thickness. + for y in range(rect.top, rect.bottom): + self.assertEqual(surface.get_at((rect.centerx, y)), ellipse_color) + + # Check horizontal thickness. + for x in range(rect.left, rect.right): + self.assertEqual(surface.get_at((x, rect.centery)), ellipse_color) + + # Check pixels above and below ellipse. + self.assertEqual( + surface.get_at((rect.centerx, rect.top - 1)), surface_color + ) + self.assertEqual( + surface.get_at((rect.centerx, rect.bottom + 1)), surface_color + ) + + # Check pixels to the left and right of the ellipse. + self.assertEqual( + surface.get_at((rect.left - 1, rect.centery)), surface_color + ) + self.assertEqual( + surface.get_at((rect.right + 1, rect.centery)), surface_color + ) + + surface.unlock() + + def _check_1_pixel_sized_ellipse( + self, surface, collide_rect, surface_color, ellipse_color + ): + # Helper method to check the surface for 1 pixel wide and/or high + # ellipses. + surf_w, surf_h = surface.get_size() + + surface.lock() # For possible speed up. + + for pos in ((x, y) for y in range(surf_h) for x in range(surf_w)): + # Since the ellipse is just a line we can use a rect to help find + # where it is expected to be drawn. + if collide_rect.collidepoint(pos): + expected_color = ellipse_color + else: + expected_color = surface_color + + self.assertEqual( + surface.get_at(pos), + expected_color, + "collide_rect={}, pos={}".format(collide_rect, pos), + ) + + surface.unlock() + + def test_ellipse__1_pixel_width(self): + """Ensures an ellipse with a width of 1 is drawn correctly. + + An ellipse with a width of 1 pixel is a vertical line. + """ + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") + surf_w, surf_h = 10, 20 + + surface = pygame.Surface((surf_w, surf_h)) + rect = pygame.Rect((0, 0), (1, 0)) + collide_rect = rect.copy() + + # Calculate some positions. + off_left = -1 + off_right = surf_w + off_bottom = surf_h + center_x = surf_w // 2 + center_y = surf_h // 2 + + # Test some even and odd heights. + for ellipse_h in range(6, 10): + # The ellipse is drawn on the edge of the rect so collide_rect + # needs +1 height to track where it's drawn. + collide_rect.h = ellipse_h + 1 + rect.h = ellipse_h + + # Calculate some variable positions. + off_top = -(ellipse_h + 1) + half_off_top = -(ellipse_h // 2) + half_off_bottom = surf_h - (ellipse_h // 2) + + # Draw the ellipse in different positions: fully on-surface, + # partially off-surface, and fully off-surface. + positions = ( + (off_left, off_top), + (off_left, half_off_top), + (off_left, center_y), + (off_left, half_off_bottom), + (off_left, off_bottom), + (center_x, off_top), + (center_x, half_off_top), + (center_x, center_y), + (center_x, half_off_bottom), + (center_x, off_bottom), + (off_right, off_top), + (off_right, half_off_top), + (off_right, center_y), + (off_right, half_off_bottom), + (off_right, off_bottom), + ) + + for rect_pos in positions: + surface.fill(surface_color) # Clear before each draw. + rect.topleft = rect_pos + collide_rect.topleft = rect_pos + + self.draw_ellipse(surface, ellipse_color, rect) + + self._check_1_pixel_sized_ellipse( + surface, collide_rect, surface_color, ellipse_color + ) + + def test_ellipse__1_pixel_width_spanning_surface(self): + """Ensures an ellipse with a width of 1 is drawn correctly + when spanning the height of the surface. + + An ellipse with a width of 1 pixel is a vertical line. + """ + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") + surf_w, surf_h = 10, 20 + + surface = pygame.Surface((surf_w, surf_h)) + rect = pygame.Rect((0, 0), (1, surf_h + 2)) # Longer than the surface. + + # Draw the ellipse in different positions: on-surface and off-surface. + positions = ( + (-1, -1), # (off_left, off_top) + (0, -1), # (left_edge, off_top) + (surf_w // 2, -1), # (center_x, off_top) + (surf_w - 1, -1), # (right_edge, off_top) + (surf_w, -1), + ) # (off_right, off_top) + + for rect_pos in positions: + surface.fill(surface_color) # Clear before each draw. + rect.topleft = rect_pos + + self.draw_ellipse(surface, ellipse_color, rect) + + self._check_1_pixel_sized_ellipse( + surface, rect, surface_color, ellipse_color + ) + + def test_ellipse__1_pixel_height(self): + """Ensures an ellipse with a height of 1 is drawn correctly. + + An ellipse with a height of 1 pixel is a horizontal line. + """ + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") + surf_w, surf_h = 20, 10 + + surface = pygame.Surface((surf_w, surf_h)) + rect = pygame.Rect((0, 0), (0, 1)) + collide_rect = rect.copy() + + # Calculate some positions. + off_right = surf_w + off_top = -1 + off_bottom = surf_h + center_x = surf_w // 2 + center_y = surf_h // 2 + + # Test some even and odd widths. + for ellipse_w in range(6, 10): + # The ellipse is drawn on the edge of the rect so collide_rect + # needs +1 width to track where it's drawn. + collide_rect.w = ellipse_w + 1 + rect.w = ellipse_w + + # Calculate some variable positions. + off_left = -(ellipse_w + 1) + half_off_left = -(ellipse_w // 2) + half_off_right = surf_w - (ellipse_w // 2) + + # Draw the ellipse in different positions: fully on-surface, + # partially off-surface, and fully off-surface. + positions = ( + (off_left, off_top), + (half_off_left, off_top), + (center_x, off_top), + (half_off_right, off_top), + (off_right, off_top), + (off_left, center_y), + (half_off_left, center_y), + (center_x, center_y), + (half_off_right, center_y), + (off_right, center_y), + (off_left, off_bottom), + (half_off_left, off_bottom), + (center_x, off_bottom), + (half_off_right, off_bottom), + (off_right, off_bottom), + ) + + for rect_pos in positions: + surface.fill(surface_color) # Clear before each draw. + rect.topleft = rect_pos + collide_rect.topleft = rect_pos + + self.draw_ellipse(surface, ellipse_color, rect) + + self._check_1_pixel_sized_ellipse( + surface, collide_rect, surface_color, ellipse_color + ) + + def test_ellipse__1_pixel_height_spanning_surface(self): + """Ensures an ellipse with a height of 1 is drawn correctly + when spanning the width of the surface. + + An ellipse with a height of 1 pixel is a horizontal line. + """ + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") + surf_w, surf_h = 20, 10 + + surface = pygame.Surface((surf_w, surf_h)) + rect = pygame.Rect((0, 0), (surf_w + 2, 1)) # Wider than the surface. + + # Draw the ellipse in different positions: on-surface and off-surface. + positions = ( + (-1, -1), # (off_left, off_top) + (-1, 0), # (off_left, top_edge) + (-1, surf_h // 2), # (off_left, center_y) + (-1, surf_h - 1), # (off_left, bottom_edge) + (-1, surf_h), + ) # (off_left, off_bottom) + + for rect_pos in positions: + surface.fill(surface_color) # Clear before each draw. + rect.topleft = rect_pos + + self.draw_ellipse(surface, ellipse_color, rect) + + self._check_1_pixel_sized_ellipse( + surface, rect, surface_color, ellipse_color + ) + + def test_ellipse__1_pixel_width_and_height(self): + """Ensures an ellipse with a width and height of 1 is drawn correctly. + + An ellipse with a width and height of 1 pixel is a single pixel. + """ + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") + surf_w, surf_h = 10, 10 + + surface = pygame.Surface((surf_w, surf_h)) + rect = pygame.Rect((0, 0), (1, 1)) + + # Calculate some positions. + off_left = -1 + off_right = surf_w + off_top = -1 + off_bottom = surf_h + left_edge = 0 + right_edge = surf_w - 1 + top_edge = 0 + bottom_edge = surf_h - 1 + center_x = surf_w // 2 + center_y = surf_h // 2 + + # Draw the ellipse in different positions: center surface, + # top/bottom/left/right edges, and off-surface. + positions = ( + (off_left, off_top), + (off_left, top_edge), + (off_left, center_y), + (off_left, bottom_edge), + (off_left, off_bottom), + (left_edge, off_top), + (left_edge, top_edge), + (left_edge, center_y), + (left_edge, bottom_edge), + (left_edge, off_bottom), + (center_x, off_top), + (center_x, top_edge), + (center_x, center_y), + (center_x, bottom_edge), + (center_x, off_bottom), + (right_edge, off_top), + (right_edge, top_edge), + (right_edge, center_y), + (right_edge, bottom_edge), + (right_edge, off_bottom), + (off_right, off_top), + (off_right, top_edge), + (off_right, center_y), + (off_right, bottom_edge), + (off_right, off_bottom), + ) + + for rect_pos in positions: + surface.fill(surface_color) # Clear before each draw. + rect.topleft = rect_pos + + self.draw_ellipse(surface, ellipse_color, rect) + + self._check_1_pixel_sized_ellipse( + surface, rect, surface_color, ellipse_color + ) + + def test_ellipse__bounding_rect(self): + """Ensures draw ellipse returns the correct bounding rect. + + Tests ellipses on and off the surface and a range of width/thickness + values. + """ + ellipse_color = pygame.Color("red") + surf_color = pygame.Color("black") + min_width = min_height = 5 + max_width = max_height = 7 + sizes = ((min_width, min_height), (max_width, max_height)) + surface = pygame.Surface((20, 20), 0, 32) + surf_rect = surface.get_rect() + # Make a rect that is bigger than the surface to help test drawing + # ellipses off and partially off the surface. + big_rect = surf_rect.inflate(min_width * 2 + 1, min_height * 2 + 1) + + for pos in rect_corners_mids_and_center( + surf_rect + ) + rect_corners_mids_and_center(big_rect): + # Each of the ellipse's rect position attributes will be set to + # the pos value. + for attr in RECT_POSITION_ATTRIBUTES: + # Test using different rect sizes and thickness values. + for width, height in sizes: + ellipse_rect = pygame.Rect((0, 0), (width, height)) + setattr(ellipse_rect, attr, pos) + + for thickness in (0, 1, 2, 3, min(width, height)): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_ellipse( + surface, ellipse_color, ellipse_rect, thickness + ) + + # Calculating the expected_rect after the ellipse + # is drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, ellipse_rect.topleft + ) + + self.assertEqual(bounding_rect, expected_rect) + + def test_ellipse__surface_clip(self): + """Ensures draw ellipse respects a surface's clip area. + + Tests drawing the ellipse filled and unfilled. + """ + surfw = surfh = 30 + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the ellipse's pos. + + for width in (0, 1): # Filled and unfilled. + # Test centering the ellipse along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the ellipse without the + # clip area set. + pos_rect.center = center + surface.set_clip(None) + surface.fill(surface_color) + self.draw_ellipse(surface, ellipse_color, pos_rect, width) + expected_pts = get_color_points(surface, ellipse_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the ellipse + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_ellipse(surface, ellipse_color, pos_rect, width) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the ellipse_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = ellipse_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() + + +class DrawEllipseTest(DrawEllipseMixin, DrawTestCase): + """Test draw module function ellipse. + + This class inherits the general tests from DrawEllipseMixin. It is also + the class to add any draw.ellipse specific tests to. + """ + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever properly supports drawing ellipses. +# @unittest.skip('draw_py.draw_ellipse not supported yet') +# class PythonDrawEllipseTest(DrawEllipseMixin, PythonDrawTestCase): +# """Test draw_py module function draw_ellipse. +# +# This class inherits the general tests from DrawEllipseMixin. It is also +# the class to add any draw_py.draw_ellipse specific tests to. +# """ + + +### Line/Lines/AALine/AALines Testing ######################################### + + +class BaseLineMixin(object): + """Mixin base for drawing various lines. + + This class contains general helper methods and setup for testing the + different types of lines. + """ + + COLORS = ( + (0, 0, 0), + (255, 0, 0), + (0, 255, 0), + (0, 0, 255), + (255, 255, 0), + (255, 0, 255), + (0, 255, 255), + (255, 255, 255), + ) + + @staticmethod + def _create_surfaces(): + # Create some surfaces with different sizes, depths, and flags. + surfaces = [] + for size in ((49, 49), (50, 50)): + for depth in (8, 16, 24, 32): + for flags in (0, SRCALPHA): + surface = pygame.display.set_mode(size, flags, depth) + surfaces.append(surface) + surfaces.append(surface.convert_alpha()) + return surfaces + + @staticmethod + def _rect_lines(rect): + # Yields pairs of end points and their reverse (to test symmetry). + # Uses a rect with the points radiating from its midleft. + for pt in rect_corners_mids_and_center(rect): + if pt == rect.midleft or pt == rect.center: + # Don't bother with these points. + continue + yield (rect.midleft, pt) + yield (pt, rect.midleft) + + +### Line Testing ############################################################## + + +class LineMixin(BaseLineMixin): + """Mixin test for drawing a single line. + + This class contains all the general single line drawing tests. + """ + + def test_line__args(self): + """Ensures draw line accepts the correct args.""" + bounds_rect = self.draw_line( + pygame.Surface((3, 3)), (0, 10, 0, 50), (0, 0), (1, 1), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__args_without_width(self): + """Ensures draw line accepts the args without a width.""" + bounds_rect = self.draw_line( + pygame.Surface((2, 2)), (0, 0, 0, 50), (0, 0), (2, 2) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__kwargs(self): + """Ensures draw line accepts the correct kwargs + with and without a width arg. + """ + surface = pygame.Surface((4, 4)) + color = pygame.Color("yellow") + start_pos = (1, 1) + end_pos = (2, 2) + kwargs_list = [ + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "width": 1, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_line(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__kwargs_order_independent(self): + """Ensures draw line's kwargs are not order dependent.""" + bounds_rect = self.draw_line( + start_pos=(1, 2), + end_pos=(2, 1), + width=2, + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__args_missing(self): + """Ensures draw line detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(surface, color, (0, 0)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line() + + def test_line__kwargs_missing(self): + """Ensures draw line detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((3, 2)), + "color": pygame.Color("red"), + "start_pos": (2, 1), + "end_pos": (2, 2), + "width": 1, + } + + for name in ("end_pos", "start_pos", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**invalid_kwargs) + + def test_line__arg_invalid_types(self): + """Ensures draw line detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + start_pos = (0, 1) + end_pos = (1, 2) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_line(surface, color, start_pos, end_pos, "1") + + with self.assertRaises(TypeError): + # Invalid end_pos. + bounds_rect = self.draw_line(surface, color, start_pos, (1, 2, 3)) + + with self.assertRaises(TypeError): + # Invalid start_pos. + bounds_rect = self.draw_line(surface, color, (1,), end_pos) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_line(surface, 2.3, start_pos, end_pos) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_line((1, 2, 3, 4), color, start_pos, end_pos) + + def test_line__kwarg_invalid_types(self): + """Ensures draw line detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + start_pos = (1, 0) + end_pos = (2, 0) + width = 1 + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "width": width, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "start_pos": start_pos, + "end_pos": end_pos, + "width": width, + }, + { + "surface": surface, + "color": color, + "start_pos": (0, 0, 0), # Invalid start_pos. + "end_pos": end_pos, + "width": width, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": (0,), # Invalid end_pos. + "width": width, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "width": 1.2, + }, + ] # Invalid width. + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**kwargs) + + def test_line__kwarg_invalid_name(self): + """Ensures draw line detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + start_pos = (1, 1) + end_pos = (2, 0) + kwargs_list = [ + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "width": 1, + "invalid": 1, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**kwargs) + + def test_line__args_and_kwargs(self): + """Ensures draw line accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 2)) + color = (255, 255, 0, 0) + start_pos = (0, 1) + end_pos = (1, 2) + width = 0 + kwargs = { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "width": width, + } + + for name in ("surface", "color", "start_pos", "end_pos", "width"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_line(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_line(surface, color, **kwargs) + elif "start_pos" == name: + bounds_rect = self.draw_line(surface, color, start_pos, **kwargs) + elif "end_pos" == name: + bounds_rect = self.draw_line( + surface, color, start_pos, end_pos, **kwargs + ) + else: + bounds_rect = self.draw_line( + surface, color, start_pos, end_pos, width, **kwargs + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__valid_width_values(self): + """Ensures draw line accepts different width values.""" + line_color = pygame.Color("yellow") + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + pos = (2, 1) + kwargs = { + "surface": surface, + "color": line_color, + "start_pos": pos, + "end_pos": (2, 2), + "width": None, + } + + for width in (-100, -10, -1, 0, 1, 10, 100): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = line_color if width > 0 else surface_color + + bounds_rect = self.draw_line(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__valid_start_pos_formats(self): + """Ensures draw line accepts different start_pos formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "start_pos": None, + "end_pos": (2, 2), + "width": 2, + } + x, y = 2, 1 # start position + + # The point values can be ints or floats. + for start_pos in ((x, y), (x + 0.1, y), (x, y + 0.1), (x + 0.1, y + 0.1)): + # The point type can be a tuple/list/Vector2. + for seq_type in (tuple, list, Vector2): + surface.fill(surface_color) # Clear for each test. + kwargs["start_pos"] = seq_type(start_pos) + + bounds_rect = self.draw_line(**kwargs) + + self.assertEqual(surface.get_at((x, y)), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__valid_end_pos_formats(self): + """Ensures draw line accepts different end_pos formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "start_pos": (2, 1), + "end_pos": None, + "width": 2, + } + x, y = 2, 2 # end position + + # The point values can be ints or floats. + for end_pos in ((x, y), (x + 0.2, y), (x, y + 0.2), (x + 0.2, y + 0.2)): + # The point type can be a tuple/list/Vector2. + for seq_type in (tuple, list, Vector2): + surface.fill(surface_color) # Clear for each test. + kwargs["end_pos"] = seq_type(end_pos) + + bounds_rect = self.draw_line(**kwargs) + + self.assertEqual(surface.get_at((x, y)), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__invalid_start_pos_formats(self): + """Ensures draw line handles invalid start_pos formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "start_pos": None, + "end_pos": (2, 2), + "width": 1, + } + + start_pos_fmts = ( + (2,), # Too few coords. + (2, 1, 0), # Too many coords. + (2, "1"), # Wrong type. + set([2, 1]), # Wrong type. + dict(((2, 1),)), + ) # Wrong type. + + for start_pos in start_pos_fmts: + kwargs["start_pos"] = start_pos + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**kwargs) + + def test_line__invalid_end_pos_formats(self): + """Ensures draw line handles invalid end_pos formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "start_pos": (2, 2), + "end_pos": None, + "width": 1, + } + + end_pos_fmts = ( + (2,), # Too few coords. + (2, 1, 0), # Too many coords. + (2, "1"), # Wrong type. + set([2, 1]), # Wrong type. + dict(((2, 1),)), + ) # Wrong type. + + for end_pos in end_pos_fmts: + kwargs["end_pos"] = end_pos + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**kwargs) + + def test_line__valid_color_formats(self): + """Ensures draw line accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + pos = (1, 1) + kwargs = { + "surface": surface, + "color": None, + "start_pos": pos, + "end_pos": (2, 1), + "width": 3, + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_line(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__invalid_color_formats(self): + """Ensures draw line handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "start_pos": (1, 1), + "end_pos": (2, 1), + "width": 1, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**kwargs) + + def test_line__color(self): + """Tests if the line drawn is the correct color.""" + pos = (0, 0) + for surface in self._create_surfaces(): + for expected_color in self.COLORS: + self.draw_line(surface, expected_color, pos, (1, 0)) + + self.assertEqual( + surface.get_at(pos), expected_color, "pos={}".format(pos) + ) + + def todo_test_line__color_with_thickness(self): + """Ensures a thick line is drawn using the correct color.""" + self.fail() + + def test_line__gaps(self): + """Tests if the line drawn contains any gaps.""" + expected_color = (255, 255, 255) + for surface in self._create_surfaces(): + width = surface.get_width() + self.draw_line(surface, expected_color, (0, 0), (width - 1, 0)) + + for x in range(width): + pos = (x, 0) + self.assertEqual( + surface.get_at(pos), expected_color, "pos={}".format(pos) + ) + + def todo_test_line__gaps_with_thickness(self): + """Ensures a thick line is drawn without any gaps.""" + self.fail() + + def test_line__bounding_rect(self): + """Ensures draw line returns the correct bounding rect. + + Tests lines with endpoints on and off the surface and a range of + width/thickness values. + """ + if isinstance(self, PythonDrawTestCase): + self.skipTest("bounding rects not supported in draw_py.draw_line") + + line_color = pygame.Color("red") + surf_color = pygame.Color("black") + width = height = 30 + # Using a rect to help manage where the lines are drawn. + helper_rect = pygame.Rect((0, 0), (width, height)) + + # Testing surfaces of different sizes. One larger than the helper_rect + # and one smaller (to test lines that span the surface). + for size in ((width + 5, height + 5), (width - 5, height - 5)): + surface = pygame.Surface(size, 0, 32) + surf_rect = surface.get_rect() + + # Move the helper rect to different positions to test line + # endpoints on and off the surface. + for pos in rect_corners_mids_and_center(surf_rect): + helper_rect.center = pos + + # Draw using different thicknesses. + for thickness in range(-1, 5): + for start, end in self._rect_lines(helper_rect): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_line( + surface, line_color, start, end, thickness + ) + + if 0 < thickness: + # Calculating the expected_rect after the line is + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, start + ) + else: + # Nothing drawn. + expected_rect = pygame.Rect(start, (0, 0)) + + self.assertEqual( + bounding_rect, + expected_rect, + "start={}, end={}, size={}, thickness={}".format( + start, end, size, thickness + ), + ) + + def test_line__surface_clip(self): + """Ensures draw line respects a surface's clip area.""" + surfw = surfh = 30 + line_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the line's pos. + + for thickness in (1, 3): # Test different line widths. + # Test centering the line along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the line without the + # clip area set. + pos_rect.center = center + surface.set_clip(None) + surface.fill(surface_color) + self.draw_line( + surface, line_color, pos_rect.midtop, pos_rect.midbottom, thickness + ) + expected_pts = get_color_points(surface, line_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the line + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_line( + surface, line_color, pos_rect.midtop, pos_rect.midbottom, thickness + ) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the line_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = line_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever fully supports drawing single lines. +# @unittest.skip('draw_py.draw_line not fully supported yet') +# class PythonDrawLineTest(LineMixin, PythonDrawTestCase): +# """Test draw_py module function line. +# +# This class inherits the general tests from LineMixin. It is also the class +# to add any draw_py.draw_line specific tests to. +# """ + + +class DrawLineTest(LineMixin, DrawTestCase): + """Test draw module function line. + + This class inherits the general tests from LineMixin. It is also the class + to add any draw.line specific tests to. + """ + + def test_line_endianness(self): + """ test color component order """ + for depth in (24, 32): + surface = pygame.Surface((5, 3), 0, depth) + surface.fill(pygame.Color(0, 0, 0)) + self.draw_line(surface, pygame.Color(255, 0, 0), (0, 1), (2, 1), 1) + + self.assertGreater(surface.get_at((1, 1)).r, 0, "there should be red here") + + surface.fill(pygame.Color(0, 0, 0)) + self.draw_line(surface, pygame.Color(0, 0, 255), (0, 1), (2, 1), 1) + + self.assertGreater(surface.get_at((1, 1)).b, 0, "there should be blue here") + + def test_line(self): + # (l, t), (l, t) + self.surf_size = (320, 200) + self.surf = pygame.Surface(self.surf_size, pygame.SRCALPHA) + self.color = (1, 13, 24, 205) + + drawn = draw.line(self.surf, self.color, (1, 0), (200, 0)) + self.assertEqual( + drawn.right, 201, "end point arg should be (or at least was) inclusive" + ) + + # Should be colored where it's supposed to be + for pt in test_utils.rect_area_pts(drawn): + self.assertEqual(self.surf.get_at(pt), self.color) + + # And not where it shouldn't + for pt in test_utils.rect_outer_bounds(drawn): + self.assertNotEqual(self.surf.get_at(pt), self.color) + + # Line width greater that 1 + line_width = 2 + offset = 5 + a = (offset, offset) + b = (self.surf_size[0] - offset, a[1]) + c = (a[0], self.surf_size[1] - offset) + d = (b[0], c[1]) + e = (a[0] + offset, c[1]) + f = (b[0], c[0] + 5) + lines = [ + (a, d), + (b, c), + (c, b), + (d, a), + (a, b), + (b, a), + (a, c), + (c, a), + (a, e), + (e, a), + (a, f), + (f, a), + (a, a), + ] + + for p1, p2 in lines: + msg = "%s - %s" % (p1, p2) + if p1[0] <= p2[0]: + plow = p1 + phigh = p2 + else: + plow = p2 + phigh = p1 + + self.surf.fill((0, 0, 0)) + rec = draw.line(self.surf, (255, 255, 255), p1, p2, line_width) + xinc = yinc = 0 + + if abs(p1[0] - p2[0]) > abs(p1[1] - p2[1]): + yinc = 1 + else: + xinc = 1 + + for i in range(line_width): + p = (p1[0] + xinc * i, p1[1] + yinc * i) + + self.assertEqual(self.surf.get_at(p), (255, 255, 255), msg) + + p = (p2[0] + xinc * i, p2[1] + yinc * i) + + self.assertEqual(self.surf.get_at(p), (255, 255, 255), msg) + + p = (plow[0] - 1, plow[1]) + + self.assertEqual(self.surf.get_at(p), (0, 0, 0), msg) + + p = (plow[0] + xinc * line_width, plow[1] + yinc * line_width) + + self.assertEqual(self.surf.get_at(p), (0, 0, 0), msg) + + p = (phigh[0] + xinc * line_width, phigh[1] + yinc * line_width) + + self.assertEqual(self.surf.get_at(p), (0, 0, 0), msg) + + if p1[0] < p2[0]: + rx = p1[0] + else: + rx = p2[0] + + if p1[1] < p2[1]: + ry = p1[1] + else: + ry = p2[1] + + w = abs(p2[0] - p1[0]) + 1 + xinc * (line_width - 1) + h = abs(p2[1] - p1[1]) + 1 + yinc * (line_width - 1) + msg += ", %s" % (rec,) + + self.assertEqual(rec, (rx, ry, w, h), msg) + + def test_line_for_gaps(self): + # This checks bug Thick Line Bug #448 + + width = 200 + height = 200 + surf = pygame.Surface((width, height), pygame.SRCALPHA) + + def white_surrounded_pixels(x, y): + offsets = [(1, 0), (0, 1), (-1, 0), (0, -1)] + WHITE = (255, 255, 255, 255) + return len( + [1 for dx, dy in offsets if surf.get_at((x + dx, y + dy)) == WHITE] + ) + + def check_white_line(start, end): + surf.fill((0, 0, 0)) + pygame.draw.line(surf, (255, 255, 255), start, end, 30) + + BLACK = (0, 0, 0, 255) + for x in range(1, width - 1): + for y in range(1, height - 1): + if surf.get_at((x, y)) == BLACK: + self.assertTrue(white_surrounded_pixels(x, y) < 3) + + check_white_line((50, 50), (140, 0)) + check_white_line((50, 50), (0, 120)) + check_white_line((50, 50), (199, 198)) + + +### Lines Testing ############################################################# + + +class LinesMixin(BaseLineMixin): + """Mixin test for drawing lines. + + This class contains all the general lines drawing tests. + """ + + def test_lines__args(self): + """Ensures draw lines accepts the correct args.""" + bounds_rect = self.draw_lines( + pygame.Surface((3, 3)), (0, 10, 0, 50), False, ((0, 0), (1, 1)), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__args_without_width(self): + """Ensures draw lines accepts the args without a width.""" + bounds_rect = self.draw_lines( + pygame.Surface((2, 2)), (0, 0, 0, 50), False, ((0, 0), (1, 1)) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__kwargs(self): + """Ensures draw lines accepts the correct kwargs + with and without a width arg. + """ + surface = pygame.Surface((4, 4)) + color = pygame.Color("yellow") + points = ((0, 0), (1, 1), (2, 2)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "closed": False, + "points": points, + "width": 1, + }, + {"surface": surface, "color": color, "closed": False, "points": points}, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_lines(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__kwargs_order_independent(self): + """Ensures draw lines's kwargs are not order dependent.""" + bounds_rect = self.draw_lines( + closed=1, + points=((0, 0), (1, 1), (2, 2)), + width=2, + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__args_missing(self): + """Ensures draw lines detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(surface, color, 0) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines() + + def test_lines__kwargs_missing(self): + """Ensures draw lines detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((3, 2)), + "color": pygame.Color("red"), + "closed": 1, + "points": ((2, 2), (1, 1)), + "width": 1, + } + + for name in ("points", "closed", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(**invalid_kwargs) + + def test_lines__arg_invalid_types(self): + """Ensures draw lines detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + closed = 0 + points = ((1, 2), (2, 1)) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_lines(surface, color, closed, points, "1") + + with self.assertRaises(TypeError): + # Invalid points. + bounds_rect = self.draw_lines(surface, color, closed, (1, 2, 3)) + + with self.assertRaises(TypeError): + # Invalid closed. + bounds_rect = self.draw_lines(surface, color, InvalidBool(), points) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_lines(surface, 2.3, closed, points) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_lines((1, 2, 3, 4), color, closed, points) + + def test_lines__kwarg_invalid_types(self): + """Ensures draw lines detects invalid kwarg types.""" + valid_kwargs = { + "surface": pygame.Surface((3, 3)), + "color": pygame.Color("green"), + "closed": False, + "points": ((1, 2), (2, 1)), + "width": 1, + } + + invalid_kwargs = { + "surface": pygame.Surface, + "color": 2.3, + "closed": InvalidBool(), + "points": (0, 0, 0), + "width": 1.2, + } + + for kwarg in ("surface", "color", "closed", "points", "width"): + kwargs = dict(valid_kwargs) + kwargs[kwarg] = invalid_kwargs[kwarg] + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(**kwargs) + + def test_lines__kwarg_invalid_name(self): + """Ensures draw lines detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + closed = 1 + points = ((1, 2), (2, 1)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + "width": 1, + "invalid": 1, + }, + { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(**kwargs) + + def test_lines__args_and_kwargs(self): + """Ensures draw lines accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 2)) + color = (255, 255, 0, 0) + closed = 0 + points = ((1, 2), (2, 1)) + width = 1 + kwargs = { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + "width": width, + } + + for name in ("surface", "color", "closed", "points", "width"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_lines(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_lines(surface, color, **kwargs) + elif "closed" == name: + bounds_rect = self.draw_lines(surface, color, closed, **kwargs) + elif "points" == name: + bounds_rect = self.draw_lines(surface, color, closed, points, **kwargs) + else: + bounds_rect = self.draw_lines( + surface, color, closed, points, width, **kwargs + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__valid_width_values(self): + """Ensures draw lines accepts different width values.""" + line_color = pygame.Color("yellow") + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + pos = (1, 1) + kwargs = { + "surface": surface, + "color": line_color, + "closed": False, + "points": (pos, (2, 1)), + "width": None, + } + + for width in (-100, -10, -1, 0, 1, 10, 100): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = line_color if width > 0 else surface_color + + bounds_rect = self.draw_lines(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__valid_points_format(self): + """Ensures draw lines accepts different points formats.""" + expected_color = (10, 20, 30, 255) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "closed": False, + "points": None, + "width": 1, + } + + # The point type can be a tuple/list/Vector2. + point_types = ( + (tuple, tuple, tuple, tuple), # all tuples + (list, list, list, list), # all lists + (Vector2, Vector2, Vector2, Vector2), # all Vector2s + (list, Vector2, tuple, Vector2), + ) # mix + + # The point values can be ints or floats. + point_values = ( + ((1, 1), (2, 1), (2, 2), (1, 2)), + ((1, 1), (2.2, 1), (2.1, 2.2), (1, 2.1)), + ) + + # Each sequence of points can be a tuple or a list. + seq_types = (tuple, list) + + for point_type in point_types: + for values in point_values: + check_pos = values[0] + points = [point_type[i](pt) for i, pt in enumerate(values)] + + for seq_type in seq_types: + surface.fill(surface_color) # Clear for each test. + kwargs["points"] = seq_type(points) + + bounds_rect = self.draw_lines(**kwargs) + + self.assertEqual(surface.get_at(check_pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__invalid_points_formats(self): + """Ensures draw lines handles invalid points formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "closed": False, + "points": None, + "width": 1, + } + + points_fmts = ( + ((1, 1), (2,)), # Too few coords. + ((1, 1), (2, 2, 2)), # Too many coords. + ((1, 1), (2, "2")), # Wrong type. + ((1, 1), set([2, 3])), # Wrong type. + ((1, 1), dict(((2, 2), (3, 3)))), # Wrong type. + set(((1, 1), (1, 2))), # Wrong type. + dict(((1, 1), (4, 4))), + ) # Wrong type. + + for points in points_fmts: + kwargs["points"] = points + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(**kwargs) + + def test_lines__invalid_points_values(self): + """Ensures draw lines handles invalid points values correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "closed": False, + "points": None, + "width": 1, + } + + for points in ([], ((1, 1),)): # Too few points. + for seq_type in (tuple, list): # Test as tuples and lists. + kwargs["points"] = seq_type(points) + + with self.assertRaises(ValueError): + bounds_rect = self.draw_lines(**kwargs) + + def test_lines__valid_closed_values(self): + """Ensures draw lines accepts different closed values.""" + line_color = pygame.Color("blue") + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + pos = (1, 2) + kwargs = { + "surface": surface, + "color": line_color, + "closed": None, + "points": ((1, 1), (3, 1), (3, 3), (1, 3)), + "width": 1, + } + + true_values = (-7, 1, 10, "2", 3.1, (4,), [5], True) + false_values = (None, "", 0, (), [], False) + + for closed in true_values + false_values: + surface.fill(surface_color) # Clear for each test. + kwargs["closed"] = closed + expected_color = line_color if closed else surface_color + + bounds_rect = self.draw_lines(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__valid_color_formats(self): + """Ensures draw lines accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + pos = (1, 1) + kwargs = { + "surface": surface, + "color": None, + "closed": False, + "points": (pos, (2, 1)), + "width": 3, + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_lines(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__invalid_color_formats(self): + """Ensures draw lines handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "closed": False, + "points": ((1, 1), (1, 2)), + "width": 1, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(**kwargs) + + def test_lines__color(self): + """Tests if the lines drawn are the correct color. + + Draws lines around the border of the given surface and checks if all + borders of the surface only contain the given color. + """ + for surface in self._create_surfaces(): + for expected_color in self.COLORS: + self.draw_lines(surface, expected_color, True, corners(surface)) + + for pos, color in border_pos_and_color(surface): + self.assertEqual(color, expected_color, "pos={}".format(pos)) + + def todo_test_lines__color_with_thickness(self): + """Ensures thick lines are drawn using the correct color.""" + self.fail() + + def test_lines__gaps(self): + """Tests if the lines drawn contain any gaps. + + Draws lines around the border of the given surface and checks if + all borders of the surface contain any gaps. + """ + expected_color = (255, 255, 255) + for surface in self._create_surfaces(): + self.draw_lines(surface, expected_color, True, corners(surface)) + + for pos, color in border_pos_and_color(surface): + self.assertEqual(color, expected_color, "pos={}".format(pos)) + + def todo_test_lines__gaps_with_thickness(self): + """Ensures thick lines are drawn without any gaps.""" + self.fail() + + def test_lines__bounding_rect(self): + """Ensures draw lines returns the correct bounding rect. + + Tests lines with endpoints on and off the surface and a range of + width/thickness values. + """ + line_color = pygame.Color("red") + surf_color = pygame.Color("black") + width = height = 30 + # Using a rect to help manage where the lines are drawn. + pos_rect = pygame.Rect((0, 0), (width, height)) + + # Testing surfaces of different sizes. One larger than the pos_rect + # and one smaller (to test lines that span the surface). + for size in ((width + 5, height + 5), (width - 5, height - 5)): + surface = pygame.Surface(size, 0, 32) + surf_rect = surface.get_rect() + + # Move pos_rect to different positions to test line endpoints on + # and off the surface. + for pos in rect_corners_mids_and_center(surf_rect): + pos_rect.center = pos + # Shape: Triangle (if closed), ^ caret (if not closed). + pts = (pos_rect.midleft, pos_rect.midtop, pos_rect.midright) + pos = pts[0] # Rect position if nothing drawn. + + # Draw using different thickness and closed values. + for thickness in range(-1, 5): + for closed in (True, False): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_lines( + surface, line_color, closed, pts, thickness + ) + + if 0 < thickness: + # Calculating the expected_rect after the lines are + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, pos + ) + else: + # Nothing drawn. + expected_rect = pygame.Rect(pos, (0, 0)) + + self.assertEqual(bounding_rect, expected_rect) + + def test_lines__surface_clip(self): + """Ensures draw lines respects a surface's clip area.""" + surfw = surfh = 30 + line_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the lines's pos. + + # Test centering the pos_rect along the clip rect's edge to allow for + # drawing the lines over the clip_rect's bounds. + for center in rect_corners_mids_and_center(clip_rect): + pos_rect.center = center + pts = (pos_rect.midtop, pos_rect.center, pos_rect.midbottom) + + for closed in (True, False): # Test closed and not closed. + for thickness in (1, 3): # Test different line widths. + # Get the expected points by drawing the lines without the + # clip area set. + surface.set_clip(None) + surface.fill(surface_color) + self.draw_lines(surface, line_color, closed, pts, thickness) + expected_pts = get_color_points(surface, line_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the lines + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_lines(surface, line_color, closed, pts, thickness) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the + # expected_pts are the line_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = line_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever fully supports drawing lines. +# class PythonDrawLinesTest(LinesMixin, PythonDrawTestCase): +# """Test draw_py module function lines. +# +# This class inherits the general tests from LinesMixin. It is also the +# class to add any draw_py.draw_lines specific tests to. +# """ + + +class DrawLinesTest(LinesMixin, DrawTestCase): + """Test draw module function lines. + + This class inherits the general tests from LinesMixin. It is also the class + to add any draw.lines specific tests to. + """ + + +### AALine Testing ############################################################ + + +class AALineMixin(BaseLineMixin): + """Mixin test for drawing a single aaline. + + This class contains all the general single aaline drawing tests. + """ + + def test_aaline__args(self): + """Ensures draw aaline accepts the correct args.""" + bounds_rect = self.draw_aaline( + pygame.Surface((3, 3)), (0, 10, 0, 50), (0, 0), (1, 1), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__args_without_blend(self): + """Ensures draw aaline accepts the args without a blend.""" + bounds_rect = self.draw_aaline( + pygame.Surface((2, 2)), (0, 0, 0, 50), (0, 0), (2, 2) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__kwargs(self): + """Ensures draw aaline accepts the correct kwargs + with and without a blend arg. + """ + surface = pygame.Surface((4, 4)) + color = pygame.Color("yellow") + start_pos = (1, 1) + end_pos = (2, 2) + kwargs_list = [ + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "blend": 1, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_aaline(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__kwargs_order_independent(self): + """Ensures draw aaline's kwargs are not order dependent.""" + bounds_rect = self.draw_aaline( + start_pos=(1, 2), + end_pos=(2, 1), + blend=1, + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__args_missing(self): + """Ensures draw aaline detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(surface, color, (0, 0)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline() + + def test_aaline__kwargs_missing(self): + """Ensures draw aaline detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((3, 2)), + "color": pygame.Color("red"), + "start_pos": (2, 1), + "end_pos": (2, 2), + "blend": 1, + } + + for name in ("end_pos", "start_pos", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**invalid_kwargs) + + def test_aaline__arg_invalid_types(self): + """Ensures draw aaline detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + start_pos = (0, 1) + end_pos = (1, 2) + + with self.assertRaises(TypeError): + # Invalid blend. + bounds_rect = self.draw_aaline(surface, color, start_pos, end_pos, "1") + + with self.assertRaises(TypeError): + # Invalid end_pos. + bounds_rect = self.draw_aaline(surface, color, start_pos, (1, 2, 3)) + + with self.assertRaises(TypeError): + # Invalid start_pos. + bounds_rect = self.draw_aaline(surface, color, (1,), end_pos) + + with self.assertRaises(ValueError): + # Invalid color. + bounds_rect = self.draw_aaline(surface, "invalid-color", start_pos, end_pos) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_aaline((1, 2, 3, 4), color, start_pos, end_pos) + + def test_aaline__kwarg_invalid_types(self): + """Ensures draw aaline detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + start_pos = (1, 0) + end_pos = (2, 0) + blend = 1 + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "blend": blend, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "start_pos": start_pos, + "end_pos": end_pos, + "blend": blend, + }, + { + "surface": surface, + "color": color, + "start_pos": (0, 0, 0), # Invalid start_pos. + "end_pos": end_pos, + "blend": blend, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": (0,), # Invalid end_pos. + "blend": blend, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "blend": 1.2, + }, + ] # Invalid blend. + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**kwargs) + + def test_aaline__kwarg_invalid_name(self): + """Ensures draw aaline detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + start_pos = (1, 1) + end_pos = (2, 0) + kwargs_list = [ + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "blend": 1, + "invalid": 1, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**kwargs) + + def test_aaline__args_and_kwargs(self): + """Ensures draw aaline accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 2)) + color = (255, 255, 0, 0) + start_pos = (0, 1) + end_pos = (1, 2) + blend = 0 + kwargs = { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "blend": blend, + } + + for name in ("surface", "color", "start_pos", "end_pos", "blend"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_aaline(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_aaline(surface, color, **kwargs) + elif "start_pos" == name: + bounds_rect = self.draw_aaline(surface, color, start_pos, **kwargs) + elif "end_pos" == name: + bounds_rect = self.draw_aaline( + surface, color, start_pos, end_pos, **kwargs + ) + else: + bounds_rect = self.draw_aaline( + surface, color, start_pos, end_pos, blend, **kwargs + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__valid_blend_values(self): + """Ensures draw aaline accepts different blend values.""" + expected_color = pygame.Color("yellow") + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + pos = (2, 1) + kwargs = { + "surface": surface, + "color": expected_color, + "start_pos": pos, + "end_pos": (2, 2), + "blend": None, + } + + for blend in (-10, -2, -1, 0, 1, 2, 10): + surface.fill(surface_color) # Clear for each test. + kwargs["blend"] = blend + + bounds_rect = self.draw_aaline(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__valid_start_pos_formats(self): + """Ensures draw aaline accepts different start_pos formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "start_pos": None, + "end_pos": (2, 2), + "blend": 0, + } + x, y = 2, 1 # start position + positions = ((x, y), (x + 0.01, y), (x, y + 0.01), (x + 0.01, y + 0.01)) + + for start_pos in positions: + for seq_type in (tuple, list, Vector2): + surface.fill(surface_color) # Clear for each test. + kwargs["start_pos"] = seq_type(start_pos) + + bounds_rect = self.draw_aaline(**kwargs) + + color = surface.get_at((x, y)) + for i, sub_color in enumerate(expected_color): + # The color could be slightly off the expected color due to + # any fractional position arguments. + self.assertGreaterEqual(color[i] + 5, sub_color, start_pos) + self.assertIsInstance(bounds_rect, pygame.Rect, start_pos) + + def test_aaline__valid_end_pos_formats(self): + """Ensures draw aaline accepts different end_pos formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "start_pos": (2, 1), + "end_pos": None, + "blend": 0, + } + x, y = 2, 2 # end position + positions = ((x, y), (x + 0.02, y), (x, y + 0.02), (x + 0.02, y + 0.02)) + + for end_pos in positions: + for seq_type in (tuple, list, Vector2): + surface.fill(surface_color) # Clear for each test. + kwargs["end_pos"] = seq_type(end_pos) + + bounds_rect = self.draw_aaline(**kwargs) + + color = surface.get_at((x, y)) + for i, sub_color in enumerate(expected_color): + # The color could be slightly off the expected color due to + # any fractional position arguments. + self.assertGreaterEqual(color[i] + 15, sub_color, end_pos) + self.assertIsInstance(bounds_rect, pygame.Rect, end_pos) + + def test_aaline__invalid_start_pos_formats(self): + """Ensures draw aaline handles invalid start_pos formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "start_pos": None, + "end_pos": (2, 2), + "blend": 0, + } + + start_pos_fmts = ( + (2,), # Too few coords. + (2, 1, 0), # Too many coords. + (2, "1"), # Wrong type. + set([2, 1]), # Wrong type. + dict(((2, 1),)), + ) # Wrong type. + + for start_pos in start_pos_fmts: + kwargs["start_pos"] = start_pos + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**kwargs) + + def test_aaline__invalid_end_pos_formats(self): + """Ensures draw aaline handles invalid end_pos formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "start_pos": (2, 2), + "end_pos": None, + "blend": 0, + } + + end_pos_fmts = ( + (2,), # Too few coords. + (2, 1, 0), # Too many coords. + (2, "1"), # Wrong type. + set([2, 1]), # Wrong type. + dict(((2, 1),)), + ) # Wrong type. + + for end_pos in end_pos_fmts: + kwargs["end_pos"] = end_pos + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**kwargs) + + def test_aaline__valid_color_formats(self): + """Ensures draw aaline accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + pos = (1, 1) + kwargs = { + "surface": surface, + "color": None, + "start_pos": pos, + "end_pos": (2, 1), + "blend": 0, + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_aaline(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__invalid_color_formats(self): + """Ensures draw aaline handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "start_pos": (1, 1), + "end_pos": (2, 1), + "blend": 0, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**kwargs) + + def test_aaline__color(self): + """Tests if the aaline drawn is the correct color.""" + pos = (0, 0) + for surface in self._create_surfaces(): + for expected_color in self.COLORS: + self.draw_aaline(surface, expected_color, pos, (1, 0)) + + self.assertEqual( + surface.get_at(pos), expected_color, "pos={}".format(pos) + ) + + def test_aaline__gaps(self): + """Tests if the aaline drawn contains any gaps. + + See: #512 + """ + expected_color = (255, 255, 255) + for surface in self._create_surfaces(): + width = surface.get_width() + self.draw_aaline(surface, expected_color, (0, 0), (width - 1, 0)) + + for x in range(width): + pos = (x, 0) + self.assertEqual( + surface.get_at(pos), expected_color, "pos={}".format(pos) + ) + + def test_aaline__bounding_rect(self): + """Ensures draw aaline returns the correct bounding rect. + + Tests lines with endpoints on and off the surface and blending + enabled and disabled. + """ + line_color = pygame.Color("red") + surf_color = pygame.Color("blue") + width = height = 30 + # Using a rect to help manage where the lines are drawn. + helper_rect = pygame.Rect((0, 0), (width, height)) + + # Testing surfaces of different sizes. One larger than the helper_rect + # and one smaller (to test lines that span the surface). + for size in ((width + 5, height + 5), (width - 5, height - 5)): + surface = pygame.Surface(size, 0, 32) + surf_rect = surface.get_rect() + + # Move the helper rect to different positions to test line + # endpoints on and off the surface. + for pos in rect_corners_mids_and_center(surf_rect): + helper_rect.center = pos + + for blend in (False, True): # Test non-blending and blending. + for start, end in self._rect_lines(helper_rect): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_aaline( + surface, line_color, start, end, blend + ) + + # Calculating the expected_rect after the line is + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect(surface, surf_color, start) + + self.assertEqual(bounding_rect, expected_rect) + + def test_aaline__surface_clip(self): + """Ensures draw aaline respects a surface's clip area.""" + surfw = surfh = 30 + aaline_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the aaline's pos. + + # Test centering the pos_rect along the clip rect's edge to allow for + # drawing the aaline over the clip_rect's bounds. + for center in rect_corners_mids_and_center(clip_rect): + pos_rect.center = center + + for blend in (0, 1): # Test non-blending and blending. + # Get the expected points by drawing the aaline without the + # clip area set. + surface.set_clip(None) + surface.fill(surface_color) + self.draw_aaline( + surface, aaline_color, pos_rect.midtop, pos_rect.midbottom, blend + ) + + # Need to get the points that are NOT surface_color due to the + # way blend=0 uses the color black to antialias. + expected_pts = get_color_points( + surface, surface_color, clip_rect, False + ) + + # Clear the surface and set the clip area. Redraw the aaline + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_aaline( + surface, aaline_color, pos_rect.midtop, pos_rect.midbottom, blend + ) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure the expected_pts + # are not surface_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + self.assertNotEqual(surface.get_at(pt), surface_color, pt) + else: + self.assertEqual(surface.get_at(pt), surface_color, pt) + + surface.unlock() + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever fully supports drawing single aalines. +# class PythonDrawAALineTest(AALineMixin, PythonDrawTestCase): +# """Test draw_py module function aaline. +# +# This class inherits the general tests from AALineMixin. It is also the +# class to add any draw_py.draw_aaline specific tests to. +# """ + + +class DrawAALineTest(AALineMixin, DrawTestCase): + """Test draw module function aaline. + + This class inherits the general tests from AALineMixin. It is also the + class to add any draw.aaline specific tests to. + """ + + def test_aaline_endianness(self): + """ test color component order """ + for depth in (24, 32): + surface = pygame.Surface((5, 3), 0, depth) + surface.fill(pygame.Color(0, 0, 0)) + self.draw_aaline(surface, pygame.Color(255, 0, 0), (0, 1), (2, 1), 1) + + self.assertGreater(surface.get_at((1, 1)).r, 0, "there should be red here") + + surface.fill(pygame.Color(0, 0, 0)) + self.draw_aaline(surface, pygame.Color(0, 0, 255), (0, 1), (2, 1), 1) + + self.assertGreater(surface.get_at((1, 1)).b, 0, "there should be blue here") + + def _check_antialiasing( + self, from_point, to_point, should, check_points, set_endpoints=True + ): + """Draw a line between two points and check colors of check_points.""" + if set_endpoints: + should[from_point] = should[to_point] = FG_GREEN + + def check_one_direction(from_point, to_point, should): + self.draw_aaline(self.surface, FG_GREEN, from_point, to_point, True) + + for pt in check_points: + color = should.get(pt, BG_RED) + if PY3: # "subTest" is sooo helpful, but does not exist in PY2 + with self.subTest(from_pt=from_point, pt=pt, to=to_point): + self.assertEqual(self.surface.get_at(pt), color) + else: + self.assertEqual(self.surface.get_at(pt), color) + + # reset + draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0) + + # it is important to test also opposite direction, the algorithm + # is (#512) or was not symmetric + check_one_direction(from_point, to_point, should) + if from_point != to_point: + check_one_direction(to_point, from_point, should) + + def test_short_non_antialiased_lines(self): + """test very short not anti aliased lines in all directions.""" + if isinstance(self, DrawTestCase): + self.skipTest("not working with draw.aaline") + + # Horizontal, vertical and diagonal lines should not be anti-aliased, + # even with draw.aaline ... + self.surface = pygame.Surface((10, 10)) + draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0) + + check_points = [(i, j) for i in range(3, 8) for j in range(3, 8)] + + def check_both_directions(from_pt, to_pt, other_points): + should = {pt: FG_GREEN for pt in other_points} + self._check_antialiasing(from_pt, to_pt, should, check_points) + + # 0. one point + check_both_directions((5, 5), (5, 5), []) + # 1. horizontal + check_both_directions((4, 7), (5, 7), []) + check_both_directions((5, 4), (7, 4), [(6, 4)]) + + # 2. vertical + check_both_directions((5, 5), (5, 6), []) + check_both_directions((6, 4), (6, 6), [(6, 5)]) + # 3. diagonals + check_both_directions((5, 5), (6, 6), []) + check_both_directions((5, 5), (7, 7), [(6, 6)]) + check_both_directions((5, 6), (6, 5), []) + check_both_directions((6, 4), (4, 6), [(5, 5)]) + + def test_short_line_anti_aliasing(self): + if isinstance(self, DrawTestCase): + self.skipTest("not working with draw.aaline") + + self.surface = pygame.Surface((10, 10)) + draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0) + + check_points = [(i, j) for i in range(3, 8) for j in range(3, 8)] + + def check_both_directions(from_pt, to_pt, should): + self._check_antialiasing(from_pt, to_pt, should, check_points) + + # lets say dx = abs(x0 - x1) ; dy = abs(y0 - y1) + brown = (127, 127, 0) + + # dy / dx = 0.5 + check_both_directions((4, 4), (6, 5), {(5, 4): brown, (5, 5): brown}) + check_both_directions((4, 5), (6, 4), {(5, 4): brown, (5, 5): brown}) + + # dy / dx = 2 + check_both_directions((4, 4), (5, 6), {(4, 5): brown, (5, 5): brown}) + check_both_directions((5, 4), (4, 6), {(4, 5): brown, (5, 5): brown}) + + # some little longer lines; so we need to check more points: + check_points = [(i, j) for i in range(2, 9) for j in range(2, 9)] + # dy / dx = 0.25 + reddish = (191, 63, 0) + greenish = (63, 191, 0) + should = { + (4, 3): greenish, + (5, 3): brown, + (6, 3): reddish, + (4, 4): reddish, + (5, 4): brown, + (6, 4): greenish, + } + check_both_directions((3, 3), (7, 4), should) + + should = { + (4, 3): reddish, + (5, 3): brown, + (6, 3): greenish, + (4, 4): greenish, + (5, 4): brown, + (6, 4): reddish, + } + check_both_directions((3, 4), (7, 3), should) + + # dy / dx = 4 + should = { + (4, 4): greenish, + (4, 5): brown, + (4, 6): reddish, + (5, 4): reddish, + (5, 5): brown, + (5, 6): greenish, + } + check_both_directions((4, 3), (5, 7), should) + + should = { + (4, 4): reddish, + (4, 5): brown, + (4, 6): greenish, + (5, 4): greenish, + (5, 5): brown, + (5, 6): reddish, + } + check_both_directions((5, 3), (4, 7), should) + + def test_anti_aliasing_float_coordinates(self): + """Float coordinates should be blended smoothly.""" + if isinstance(self, DrawTestCase): + self.skipTest("not working with draw.aaline") + + self.surface = pygame.Surface((10, 10)) + draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0) + + check_points = [(i, j) for i in range(5) for j in range(5)] + brown = (127, 127, 0) + + # 0. identical point : current implementation does no smoothing... + expected = {(1, 2): FG_GREEN} + self._check_antialiasing( + (1.5, 2), (1.5, 2), expected, check_points, set_endpoints=False + ) + expected = {(2, 2): FG_GREEN} + self._check_antialiasing( + (2.5, 2.7), (2.5, 2.7), expected, check_points, set_endpoints=False + ) + + # 1. horizontal lines + # a) blend endpoints + expected = {(1, 2): brown, (2, 2): FG_GREEN} + self._check_antialiasing( + (1.5, 2), (2, 2), expected, check_points, set_endpoints=False + ) + expected = {(1, 2): brown, (2, 2): FG_GREEN, (3, 2): brown} + self._check_antialiasing( + (1.5, 2), (2.5, 2), expected, check_points, set_endpoints=False + ) + expected = {(2, 2): brown, (1, 2): FG_GREEN} + self._check_antialiasing( + (1, 2), (1.5, 2), expected, check_points, set_endpoints=False + ) + expected = {(1, 2): brown, (2, 2): (63, 191, 0)} + self._check_antialiasing( + (1.5, 2), (1.75, 2), expected, check_points, set_endpoints=False + ) + + # b) blend y-coordinate + expected = {(x, y): brown for x in range(2, 5) for y in (1, 2)} + self._check_antialiasing( + (2, 1.5), (4, 1.5), expected, check_points, set_endpoints=False + ) + + # 2. vertical lines + # a) blend endpoints + expected = {(2, 1): brown, (2, 2): FG_GREEN, (2, 3): brown} + self._check_antialiasing( + (2, 1.5), (2, 2.5), expected, check_points, set_endpoints=False + ) + expected = {(2, 1): brown, (2, 2): (63, 191, 0)} + self._check_antialiasing( + (2, 1.5), (2, 1.75), expected, check_points, set_endpoints=False + ) + # b) blend x-coordinate + expected = {(x, y): brown for x in (1, 2) for y in range(2, 5)} + self._check_antialiasing( + (1.5, 2), (1.5, 4), expected, check_points, set_endpoints=False + ) + # 3. diagonal lines + # a) blend endpoints + expected = {(1, 1): brown, (2, 2): FG_GREEN, (3, 3): brown} + self._check_antialiasing( + (1.5, 1.5), (2.5, 2.5), expected, check_points, set_endpoints=False + ) + expected = {(3, 1): brown, (2, 2): FG_GREEN, (1, 3): brown} + self._check_antialiasing( + (2.5, 1.5), (1.5, 2.5), expected, check_points, set_endpoints=False + ) + # b) blend sidewards + expected = {(2, 1): brown, (2, 2): brown, (3, 2): brown, (3, 3): brown} + self._check_antialiasing( + (2, 1.5), (3, 2.5), expected, check_points, set_endpoints=False + ) + + reddish = (191, 63, 0) + greenish = (63, 191, 0) + expected = { + (2, 1): greenish, + (2, 2): reddish, + (3, 2): greenish, + (3, 3): reddish, + (4, 3): greenish, + (4, 4): reddish, + } + + self._check_antialiasing( + (2, 1.25), (4, 3.25), expected, check_points, set_endpoints=False + ) + + def test_anti_aliasing_at_and_outside_the_border(self): + """Ensures antialiasing works correct at a surface's borders.""" + if isinstance(self, DrawTestCase): + self.skipTest("not working with draw.aaline") + + self.surface = pygame.Surface((10, 10)) + draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0) + + check_points = [(i, j) for i in range(10) for j in range(10)] + + reddish = (191, 63, 0) + brown = (127, 127, 0) + greenish = (63, 191, 0) + from_point, to_point = (3, 3), (7, 4) + should = { + (4, 3): greenish, + (5, 3): brown, + (6, 3): reddish, + (4, 4): reddish, + (5, 4): brown, + (6, 4): greenish, + } + + for dx, dy in ( + (-4, 0), + (4, 0), # moved to left and right borders + (0, -5), + (0, -4), + (0, -3), # upper border + (0, 5), + (0, 6), + (0, 7), # lower border + (-4, -4), + (-4, -3), + (-3, -4), + ): # upper left corner + first = from_point[0] + dx, from_point[1] + dy + second = to_point[0] + dx, to_point[1] + dy + expected = {(x + dx, y + dy): color for (x, y), color in should.items()} + + self._check_antialiasing(first, second, expected, check_points) + + +### AALines Testing ########################################################### + + +class AALinesMixin(BaseLineMixin): + """Mixin test for drawing aalines. + + This class contains all the general aalines drawing tests. + """ + + def test_aalines__args(self): + """Ensures draw aalines accepts the correct args.""" + bounds_rect = self.draw_aalines( + pygame.Surface((3, 3)), (0, 10, 0, 50), False, ((0, 0), (1, 1)), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__args_without_blend(self): + """Ensures draw aalines accepts the args without a blend.""" + bounds_rect = self.draw_aalines( + pygame.Surface((2, 2)), (0, 0, 0, 50), False, ((0, 0), (1, 1)) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__kwargs(self): + """Ensures draw aalines accepts the correct kwargs + with and without a blend arg. + """ + surface = pygame.Surface((4, 4)) + color = pygame.Color("yellow") + points = ((0, 0), (1, 1), (2, 2)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "closed": False, + "points": points, + "blend": 1, + }, + {"surface": surface, "color": color, "closed": False, "points": points}, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_aalines(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__kwargs_order_independent(self): + """Ensures draw aalines's kwargs are not order dependent.""" + bounds_rect = self.draw_aalines( + closed=1, + points=((0, 0), (1, 1), (2, 2)), + blend=1, + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__args_missing(self): + """Ensures draw aalines detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(surface, color, 0) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines() + + def test_aalines__kwargs_missing(self): + """Ensures draw aalines detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((3, 2)), + "color": pygame.Color("red"), + "closed": 1, + "points": ((2, 2), (1, 1)), + "blend": 1, + } + + for name in ("points", "closed", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(**invalid_kwargs) + + def test_aalines__arg_invalid_types(self): + """Ensures draw aalines detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + closed = 0 + points = ((1, 2), (2, 1)) + + with self.assertRaises(TypeError): + # Invalid blend. + bounds_rect = self.draw_aalines(surface, color, closed, points, "1") + + with self.assertRaises(TypeError): + # Invalid points. + bounds_rect = self.draw_aalines(surface, color, closed, (1, 2, 3)) + + with self.assertRaises(TypeError): + # Invalid closed. + bounds_rect = self.draw_aalines(surface, color, InvalidBool(), points) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_aalines(surface, 2.3, closed, points) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_aalines((1, 2, 3, 4), color, closed, points) + + def test_aalines__kwarg_invalid_types(self): + """Ensures draw aalines detects invalid kwarg types.""" + valid_kwargs = { + "surface": pygame.Surface((3, 3)), + "color": pygame.Color("green"), + "closed": False, + "points": ((1, 2), (2, 1)), + "blend": 1, + } + + invalid_kwargs = { + "surface": pygame.Surface, + "color": 2.3, + "closed": InvalidBool(), + "points": (0, 0, 0), + "blend": 1.2, + } + + for kwarg in ("surface", "color", "closed", "points", "blend"): + kwargs = dict(valid_kwargs) + kwargs[kwarg] = invalid_kwargs[kwarg] + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(**kwargs) + + def test_aalines__kwarg_invalid_name(self): + """Ensures draw aalines detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + closed = 1 + points = ((1, 2), (2, 1)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + "blend": 1, + "invalid": 1, + }, + { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(**kwargs) + + def test_aalines__args_and_kwargs(self): + """Ensures draw aalines accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 2)) + color = (255, 255, 0, 0) + closed = 0 + points = ((1, 2), (2, 1)) + blend = 1 + kwargs = { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + "blend": blend, + } + + for name in ("surface", "color", "closed", "points", "blend"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_aalines(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_aalines(surface, color, **kwargs) + elif "closed" == name: + bounds_rect = self.draw_aalines(surface, color, closed, **kwargs) + elif "points" == name: + bounds_rect = self.draw_aalines( + surface, color, closed, points, **kwargs + ) + else: + bounds_rect = self.draw_aalines( + surface, color, closed, points, blend, **kwargs + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__valid_blend_values(self): + """Ensures draw aalines accepts different blend values.""" + expected_color = pygame.Color("yellow") + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + pos = (1, 1) + kwargs = { + "surface": surface, + "color": expected_color, + "closed": False, + "points": (pos, (1, 3)), + "blend": None, + } + + for blend in (-10, -2, -1, 0, 1, 2, 10): + surface.fill(surface_color) # Clear for each test. + kwargs["blend"] = blend + + bounds_rect = self.draw_aalines(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color, blend) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__valid_points_format(self): + """Ensures draw aalines accepts different points formats.""" + expected_color = (10, 20, 30, 255) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "closed": False, + "points": None, + "blend": 0, + } + + # The point type can be a tuple/list/Vector2. + point_types = ( + (tuple, tuple, tuple, tuple), # all tuples + (list, list, list, list), # all lists + (Vector2, Vector2, Vector2, Vector2), # all Vector2s + (list, Vector2, tuple, Vector2), + ) # mix + + # The point values can be ints or floats. + point_values = ( + ((1, 1), (2, 1), (2, 2), (1, 2)), + ((1, 1), (2.2, 1), (2.1, 2.2), (1, 2.1)), + ) + + # Each sequence of points can be a tuple or a list. + seq_types = (tuple, list) + + for point_type in point_types: + for values in point_values: + check_pos = values[0] + points = [point_type[i](pt) for i, pt in enumerate(values)] + + for seq_type in seq_types: + surface.fill(surface_color) # Clear for each test. + kwargs["points"] = seq_type(points) + + bounds_rect = self.draw_aalines(**kwargs) + + self.assertEqual(surface.get_at(check_pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__invalid_points_formats(self): + """Ensures draw aalines handles invalid points formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "closed": False, + "points": None, + "blend": 1, + } + + points_fmts = ( + ((1, 1), (2,)), # Too few coords. + ((1, 1), (2, 2, 2)), # Too many coords. + ((1, 1), (2, "2")), # Wrong type. + ((1, 1), set([2, 3])), # Wrong type. + ((1, 1), dict(((2, 2), (3, 3)))), # Wrong type. + set(((1, 1), (1, 2))), # Wrong type. + dict(((1, 1), (4, 4))), + ) # Wrong type. + + for points in points_fmts: + kwargs["points"] = points + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(**kwargs) + + def test_aalines__invalid_points_values(self): + """Ensures draw aalines handles invalid points values correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "closed": False, + "points": None, + "blend": 1, + } + + for points in ([], ((1, 1),)): # Too few points. + for seq_type in (tuple, list): # Test as tuples and lists. + kwargs["points"] = seq_type(points) + + with self.assertRaises(ValueError): + bounds_rect = self.draw_aalines(**kwargs) + + def test_aalines__valid_closed_values(self): + """Ensures draw aalines accepts different closed values.""" + line_color = pygame.Color("blue") + surface_color = pygame.Color("white") + surface = pygame.Surface((5, 5)) + pos = (1, 3) + kwargs = { + "surface": surface, + "color": line_color, + "closed": None, + "points": ((1, 1), (4, 1), (4, 4), (1, 4)), + "blend": 0, + } + + true_values = (-7, 1, 10, "2", 3.1, (4,), [5], True) + false_values = (None, "", 0, (), [], False) + + for closed in true_values + false_values: + surface.fill(surface_color) # Clear for each test. + kwargs["closed"] = closed + expected_color = line_color if closed else surface_color + + bounds_rect = self.draw_aalines(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__valid_color_formats(self): + """Ensures draw aalines accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + pos = (1, 1) + kwargs = { + "surface": surface, + "color": None, + "closed": False, + "points": (pos, (2, 1)), + "blend": 0, + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_aalines(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__invalid_color_formats(self): + """Ensures draw aalines handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "closed": False, + "points": ((1, 1), (1, 2)), + "blend": 0, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(**kwargs) + + def test_aalines__color(self): + """Tests if the aalines drawn are the correct color. + + Draws aalines around the border of the given surface and checks if all + borders of the surface only contain the given color. + """ + for surface in self._create_surfaces(): + for expected_color in self.COLORS: + self.draw_aalines(surface, expected_color, True, corners(surface)) + + for pos, color in border_pos_and_color(surface): + self.assertEqual(color, expected_color, "pos={}".format(pos)) + + def test_aalines__gaps(self): + """Tests if the aalines drawn contain any gaps. + + Draws aalines around the border of the given surface and checks if + all borders of the surface contain any gaps. + + See: #512 + """ + expected_color = (255, 255, 255) + for surface in self._create_surfaces(): + self.draw_aalines(surface, expected_color, True, corners(surface)) + + for pos, color in border_pos_and_color(surface): + self.assertEqual(color, expected_color, "pos={}".format(pos)) + + def test_aalines__bounding_rect(self): + """Ensures draw aalines returns the correct bounding rect. + + Tests lines with endpoints on and off the surface and blending + enabled and disabled. + """ + line_color = pygame.Color("red") + surf_color = pygame.Color("blue") + width = height = 30 + # Using a rect to help manage where the lines are drawn. + pos_rect = pygame.Rect((0, 0), (width, height)) + + # Testing surfaces of different sizes. One larger than the pos_rect + # and one smaller (to test lines that span the surface). + for size in ((width + 5, height + 5), (width - 5, height - 5)): + surface = pygame.Surface(size, 0, 32) + surf_rect = surface.get_rect() + + # Move pos_rect to different positions to test line endpoints on + # and off the surface. + for pos in rect_corners_mids_and_center(surf_rect): + pos_rect.center = pos + # Shape: Triangle (if closed), ^ caret (if not closed). + pts = (pos_rect.midleft, pos_rect.midtop, pos_rect.midright) + pos = pts[0] # Rect position if nothing drawn. + + for blend in (False, True): # Test non-blending and blending. + for closed in (True, False): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_aalines( + surface, line_color, closed, pts, blend + ) + + # Calculating the expected_rect after the lines are + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect(surface, surf_color, pos) + + self.assertEqual(bounding_rect, expected_rect) + + def test_aalines__surface_clip(self): + """Ensures draw aalines respects a surface's clip area.""" + surfw = surfh = 30 + aaline_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the aalines's pos. + + # Test centering the pos_rect along the clip rect's edge to allow for + # drawing the aalines over the clip_rect's bounds. + for center in rect_corners_mids_and_center(clip_rect): + pos_rect.center = center + pts = (pos_rect.midtop, pos_rect.center, pos_rect.midbottom) + + for closed in (True, False): # Test closed and not closed. + for blend in (0, 1): # Test non-blending and blending. + # Get the expected points by drawing the aalines without + # the clip area set. + surface.set_clip(None) + surface.fill(surface_color) + self.draw_aalines(surface, aaline_color, closed, pts, blend) + + # Need to get the points that are NOT surface_color due to + # the way blend=0 uses the color black to antialias. + expected_pts = get_color_points( + surface, surface_color, clip_rect, False + ) + + # Clear the surface and set the clip area. Redraw the + # aalines and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_aalines(surface, aaline_color, closed, pts, blend) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure the expected_pts + # are not surface_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + self.assertNotEqual(surface.get_at(pt), surface_color, pt) + else: + self.assertEqual(surface.get_at(pt), surface_color, pt) + + surface.unlock() + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever fully supports drawing aalines. +# class PythonDrawAALinesTest(AALinesMixin, PythonDrawTestCase): +# """Test draw_py module function aalines. +# +# This class inherits the general tests from AALinesMixin. It is also the +# class to add any draw_py.draw_aalines specific tests to. +# """ + + +class DrawAALinesTest(AALinesMixin, DrawTestCase): + """Test draw module function aalines. + + This class inherits the general tests from AALinesMixin. It is also the + class to add any draw.aalines specific tests to. + """ + + +### Polygon Testing ########################################################### + +SQUARE = ([0, 0], [3, 0], [3, 3], [0, 3]) +DIAMOND = [(1, 3), (3, 5), (5, 3), (3, 1)] +CROSS = ( + [2, 0], + [4, 0], + [4, 2], + [6, 2], + [6, 4], + [4, 4], + [4, 6], + [2, 6], + [2, 4], + [0, 4], + [0, 2], + [2, 2], +) + + +class DrawPolygonMixin(object): + """Mixin tests for drawing polygons. + + This class contains all the general polygon drawing tests. + """ + + def setUp(self): + self.surface = pygame.Surface((20, 20)) + + def test_polygon__args(self): + """Ensures draw polygon accepts the correct args.""" + bounds_rect = self.draw_polygon( + pygame.Surface((3, 3)), (0, 10, 0, 50), ((0, 0), (1, 1), (2, 2)), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__args_without_width(self): + """Ensures draw polygon accepts the args without a width.""" + bounds_rect = self.draw_polygon( + pygame.Surface((2, 2)), (0, 0, 0, 50), ((0, 0), (1, 1), (2, 2)) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__kwargs(self): + """Ensures draw polygon accepts the correct kwargs + with and without a width arg. + """ + surface = pygame.Surface((4, 4)) + color = pygame.Color("yellow") + points = ((0, 0), (1, 1), (2, 2)) + kwargs_list = [ + {"surface": surface, "color": color, "points": points, "width": 1}, + {"surface": surface, "color": color, "points": points}, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_polygon(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__kwargs_order_independent(self): + """Ensures draw polygon's kwargs are not order dependent.""" + bounds_rect = self.draw_polygon( + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + width=0, + points=((0, 1), (1, 2), (2, 3)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__args_missing(self): + """Ensures draw polygon detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon() + + def test_polygon__kwargs_missing(self): + """Ensures draw polygon detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((1, 2)), + "color": pygame.Color("red"), + "points": ((2, 1), (2, 2), (2, 3)), + "width": 1, + } + + for name in ("points", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(**invalid_kwargs) + + def test_polygon__arg_invalid_types(self): + """Ensures draw polygon detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + points = ((0, 1), (1, 2), (1, 3)) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_polygon(surface, color, points, "1") + + with self.assertRaises(TypeError): + # Invalid points. + bounds_rect = self.draw_polygon(surface, color, (1, 2, 3)) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_polygon(surface, 2.3, points) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_polygon((1, 2, 3, 4), color, points) + + def test_polygon__kwarg_invalid_types(self): + """Ensures draw polygon detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + points = ((0, 0), (1, 0), (2, 0)) + width = 1 + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "points": points, + "width": width, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "points": points, + "width": width, + }, + { + "surface": surface, + "color": color, + "points": ((1,), (1,), (1,)), # Invalid points. + "width": width, + }, + {"surface": surface, "color": color, "points": points, "width": 1.2}, + ] # Invalid width. + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(**kwargs) + + def test_polygon__kwarg_invalid_name(self): + """Ensures draw polygon detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + points = ((1, 1), (1, 2), (1, 3)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "points": points, + "width": 1, + "invalid": 1, + }, + {"surface": surface, "color": color, "points": points, "invalid": 1}, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(**kwargs) + + def test_polygon__args_and_kwargs(self): + """Ensures draw polygon accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 1)) + color = (255, 255, 0, 0) + points = ((0, 1), (1, 2), (2, 3)) + width = 0 + kwargs = {"surface": surface, "color": color, "points": points, "width": width} + + for name in ("surface", "color", "points", "width"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_polygon(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_polygon(surface, color, **kwargs) + elif "points" == name: + bounds_rect = self.draw_polygon(surface, color, points, **kwargs) + else: + bounds_rect = self.draw_polygon(surface, color, points, width, **kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__valid_width_values(self): + """Ensures draw polygon accepts different width values.""" + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + color = (10, 20, 30, 255) + kwargs = { + "surface": surface, + "color": color, + "points": ((1, 1), (2, 1), (2, 2), (1, 2)), + "width": None, + } + pos = kwargs["points"][0] + + for width in (-100, -10, -1, 0, 1, 10, 100): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = color if width >= 0 else surface_color + + bounds_rect = self.draw_polygon(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__valid_points_format(self): + """Ensures draw polygon accepts different points formats.""" + expected_color = (10, 20, 30, 255) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "points": None, + "width": 0, + } + + # The point type can be a tuple/list/Vector2. + point_types = ( + (tuple, tuple, tuple, tuple), # all tuples + (list, list, list, list), # all lists + (Vector2, Vector2, Vector2, Vector2), # all Vector2s + (list, Vector2, tuple, Vector2), + ) # mix + + # The point values can be ints or floats. + point_values = ( + ((1, 1), (2, 1), (2, 2), (1, 2)), + ((1, 1), (2.2, 1), (2.1, 2.2), (1, 2.1)), + ) + + # Each sequence of points can be a tuple or a list. + seq_types = (tuple, list) + + for point_type in point_types: + for values in point_values: + check_pos = values[0] + points = [point_type[i](pt) for i, pt in enumerate(values)] + + for seq_type in seq_types: + surface.fill(surface_color) # Clear for each test. + kwargs["points"] = seq_type(points) + + bounds_rect = self.draw_polygon(**kwargs) + + self.assertEqual(surface.get_at(check_pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__invalid_points_formats(self): + """Ensures draw polygon handles invalid points formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "points": None, + "width": 0, + } + + points_fmts = ( + ((1, 1), (2, 1), (2,)), # Too few coords. + ((1, 1), (2, 1), (2, 2, 2)), # Too many coords. + ((1, 1), (2, 1), (2, "2")), # Wrong type. + ((1, 1), (2, 1), set([2, 3])), # Wrong type. + ((1, 1), (2, 1), dict(((2, 2), (3, 3)))), # Wrong type. + set(((1, 1), (2, 1), (2, 2), (1, 2))), # Wrong type. + dict(((1, 1), (2, 2), (3, 3), (4, 4))), + ) # Wrong type. + + for points in points_fmts: + kwargs["points"] = points + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(**kwargs) + + def test_polygon__invalid_points_values(self): + """Ensures draw polygon handles invalid points values correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "points": None, + "width": 0, + } + + points_fmts = ( + tuple(), # Too few points. + ((1, 1),), # Too few points. + ((1, 1), (2, 1)), + ) # Too few points. + + for points in points_fmts: + for seq_type in (tuple, list): # Test as tuples and lists. + kwargs["points"] = seq_type(points) + + with self.assertRaises(ValueError): + bounds_rect = self.draw_polygon(**kwargs) + + def test_polygon__valid_color_formats(self): + """Ensures draw polygon accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": None, + "points": ((1, 1), (2, 1), (2, 2), (1, 2)), + "width": 0, + } + pos = kwargs["points"][0] + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_polygon(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__invalid_color_formats(self): + """Ensures draw polygon handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "points": ((1, 1), (2, 1), (2, 2), (1, 2)), + "width": 0, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(**kwargs) + + def test_draw_square(self): + self.draw_polygon(self.surface, RED, SQUARE, 0) + # note : there is a discussion (#234) if draw.polygon should include or + # not the right or lower border; here we stick with current behavior, + # eg include those borders ... + for x in range(4): + for y in range(4): + self.assertEqual(self.surface.get_at((x, y)), RED) + + def test_draw_diamond(self): + pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0) + self.draw_polygon(self.surface, GREEN, DIAMOND, 0) + # this diamond shape is equivalent to its four corners, plus inner square + for x, y in DIAMOND: + self.assertEqual(self.surface.get_at((x, y)), GREEN, msg=str((x, y))) + for x in range(2, 5): + for y in range(2, 5): + self.assertEqual(self.surface.get_at((x, y)), GREEN) + + def test_1_pixel_high_or_wide_shapes(self): + # 1. one-pixel-high, filled + pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0) + self.draw_polygon(self.surface, GREEN, [(x, 2) for x, _y in CROSS], 0) + cross_size = 6 # the maximum x or y coordinate of the cross + for x in range(cross_size + 1): + self.assertEqual(self.surface.get_at((x, 1)), RED) + self.assertEqual(self.surface.get_at((x, 2)), GREEN) + self.assertEqual(self.surface.get_at((x, 3)), RED) + pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0) + # 2. one-pixel-high, not filled + self.draw_polygon(self.surface, GREEN, [(x, 5) for x, _y in CROSS], 1) + for x in range(cross_size + 1): + self.assertEqual(self.surface.get_at((x, 4)), RED) + self.assertEqual(self.surface.get_at((x, 5)), GREEN) + self.assertEqual(self.surface.get_at((x, 6)), RED) + pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0) + # 3. one-pixel-wide, filled + self.draw_polygon(self.surface, GREEN, [(3, y) for _x, y in CROSS], 0) + for y in range(cross_size + 1): + self.assertEqual(self.surface.get_at((2, y)), RED) + self.assertEqual(self.surface.get_at((3, y)), GREEN) + self.assertEqual(self.surface.get_at((4, y)), RED) + pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0) + # 4. one-pixel-wide, not filled + self.draw_polygon(self.surface, GREEN, [(4, y) for _x, y in CROSS], 1) + for y in range(cross_size + 1): + self.assertEqual(self.surface.get_at((3, y)), RED) + self.assertEqual(self.surface.get_at((4, y)), GREEN) + self.assertEqual(self.surface.get_at((5, y)), RED) + + def test_draw_symetric_cross(self): + """non-regression on issue #234 : x and y where handled inconsistently. + + Also, the result is/was different whether we fill or not the polygon. + """ + # 1. case width = 1 (not filled: `polygon` calls internally the `lines` function) + pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0) + self.draw_polygon(self.surface, GREEN, CROSS, 1) + inside = [(x, 3) for x in range(1, 6)] + [(3, y) for y in range(1, 6)] + for x in range(10): + for y in range(10): + if (x, y) in inside: + self.assertEqual(self.surface.get_at((x, y)), RED) + elif (x in range(2, 5) and y < 7) or (y in range(2, 5) and x < 7): + # we are on the border of the cross: + self.assertEqual(self.surface.get_at((x, y)), GREEN) + else: + # we are outside + self.assertEqual(self.surface.get_at((x, y)), RED) + + # 2. case width = 0 (filled; this is the example from #234) + pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0) + self.draw_polygon(self.surface, GREEN, CROSS, 0) + inside = [(x, 3) for x in range(1, 6)] + [(3, y) for y in range(1, 6)] + for x in range(10): + for y in range(10): + if (x in range(2, 5) and y < 7) or (y in range(2, 5) and x < 7): + # we are on the border of the cross: + self.assertEqual( + self.surface.get_at((x, y)), GREEN, msg=str((x, y)) + ) + else: + # we are outside + self.assertEqual(self.surface.get_at((x, y)), RED) + + def test_illumine_shape(self): + """non-regression on issue #313""" + rect = pygame.Rect((0, 0, 20, 20)) + path_data = [ + (0, 0), + (rect.width - 1, 0), # upper border + (rect.width - 5, 5 - 1), + (5 - 1, 5 - 1), # upper inner + (5 - 1, rect.height - 5), + (0, rect.height - 1), + ] # lower diagonal + # The shape looks like this (the numbers are the indices of path_data) + + # 0**********************1 <-- upper border + # *********************** + # ********************** + # ********************* + # ****3**************2 <-- upper inner border + # ***** + # ***** (more lines here) + # ***** + # ****4 + # **** + # *** + # ** + # 5 + # + + # the current bug is that the "upper inner" line is not drawn, but only + # if 4 or some lower corner exists + pygame.draw.rect(self.surface, RED, (0, 0, 20, 20), 0) + + # 1. First without the corners 4 & 5 + self.draw_polygon(self.surface, GREEN, path_data[:4], 0) + for x in range(20): + self.assertEqual(self.surface.get_at((x, 0)), GREEN) # upper border + for x in range(4, rect.width - 5 + 1): + self.assertEqual(self.surface.get_at((x, 4)), GREEN) # upper inner + + # 2. with the corners 4 & 5 + pygame.draw.rect(self.surface, RED, (0, 0, 20, 20), 0) + self.draw_polygon(self.surface, GREEN, path_data, 0) + for x in range(4, rect.width - 5 + 1): + self.assertEqual(self.surface.get_at((x, 4)), GREEN) # upper inner + + def test_invalid_points(self): + self.assertRaises( + TypeError, + lambda: self.draw_polygon( + self.surface, RED, ((0, 0), (0, 20), (20, 20), 20), 0 + ), + ) + + def test_polygon__bounding_rect(self): + """Ensures draw polygon returns the correct bounding rect. + + Tests polygons on and off the surface and a range of width/thickness + values. + """ + polygon_color = pygame.Color("red") + surf_color = pygame.Color("black") + min_width = min_height = 5 + max_width = max_height = 7 + sizes = ((min_width, min_height), (max_width, max_height)) + surface = pygame.Surface((20, 20), 0, 32) + surf_rect = surface.get_rect() + # Make a rect that is bigger than the surface to help test drawing + # polygons off and partially off the surface. + big_rect = surf_rect.inflate(min_width * 2 + 1, min_height * 2 + 1) + + for pos in rect_corners_mids_and_center( + surf_rect + ) + rect_corners_mids_and_center(big_rect): + # A rect (pos_rect) is used to help create and position the + # polygon. Each of this rect's position attributes will be set to + # the pos value. + for attr in RECT_POSITION_ATTRIBUTES: + # Test using different rect sizes and thickness values. + for width, height in sizes: + pos_rect = pygame.Rect((0, 0), (width, height)) + setattr(pos_rect, attr, pos) + # Points form a triangle with no fully + # horizontal/vertical lines. + vertices = ( + pos_rect.midleft, + pos_rect.midtop, + pos_rect.bottomright, + ) + + for thickness in range(4): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_polygon( + surface, polygon_color, vertices, thickness + ) + + # Calculating the expected_rect after the polygon + # is drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, vertices[0] + ) + + self.assertEqual( + bounding_rect, + expected_rect, + "thickness={}".format(thickness), + ) + + def test_polygon__surface_clip(self): + """Ensures draw polygon respects a surface's clip area. + + Tests drawing the polygon filled and unfilled. + """ + surfw = surfh = 30 + polygon_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (8, 10)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the polygon's pos. + + for width in (0, 1): # Filled and unfilled. + # Test centering the polygon along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the polygon without the + # clip area set. + pos_rect.center = center + vertices = ( + pos_rect.topleft, + pos_rect.topright, + pos_rect.bottomright, + pos_rect.bottomleft, + ) + surface.set_clip(None) + surface.fill(surface_color) + self.draw_polygon(surface, polygon_color, vertices, width) + expected_pts = get_color_points(surface, polygon_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the polygon + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_polygon(surface, polygon_color, vertices, width) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the polygon_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = polygon_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() + + +class DrawPolygonTest(DrawPolygonMixin, DrawTestCase): + """Test draw module function polygon. + + This class inherits the general tests from DrawPolygonMixin. It is also + the class to add any draw.polygon specific tests to. + """ + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever fully supports drawing polygons. +# @unittest.skip('draw_py.draw_polygon not fully supported yet') +# class PythonDrawPolygonTest(DrawPolygonMixin, PythonDrawTestCase): +# """Test draw_py module function draw_polygon. +# +# This class inherits the general tests from DrawPolygonMixin. It is also +# the class to add any draw_py.draw_polygon specific tests to. +# """ + + +### Rect Testing ############################################################## + + +class DrawRectMixin(object): + """Mixin tests for drawing rects. + + This class contains all the general rect drawing tests. + """ + + def test_rect__args(self): + """Ensures draw rect accepts the correct args.""" + bounds_rect = self.draw_rect( + pygame.Surface((2, 2)), (20, 10, 20, 150), pygame.Rect((0, 0), (1, 1)), 2, 1, 2, 3, 4, 5 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__args_without_width(self): + """Ensures draw rect accepts the args without a width and borders.""" + bounds_rect = self.draw_rect( + pygame.Surface((3, 5)), (0, 0, 0, 255), pygame.Rect((0, 0), (1, 1)) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__kwargs(self): + """Ensures draw rect accepts the correct kwargs + with and without a width and border_radius arg. + """ + kwargs_list = [ + { + "surface": pygame.Surface((5, 5)), + "color": pygame.Color("red"), + "rect": pygame.Rect((0, 0), (1, 2)), + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0 + }, + { + "surface": pygame.Surface((1, 2)), + "color": (0, 100, 200), + "rect": (0, 0, 1, 1), + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_rect(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__kwargs_order_independent(self): + """Ensures draw rect's kwargs are not order dependent.""" + bounds_rect = self.draw_rect( + color=(0, 1, 2), + border_radius=10, + surface=pygame.Surface((2, 3)), + border_top_left_radius=5, + width=-2, + border_top_right_radius=20, + border_bottom_right_radius=0, + rect=pygame.Rect((0, 0), (0, 0)), + border_bottom_left_radius=15, + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__args_missing(self): + """Ensures draw rect detects any missing required args.""" + surface = pygame.Surface((1, 1)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(surface, pygame.Color("white")) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect() + + def test_rect__kwargs_missing(self): + """Ensures draw rect detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((1, 3)), + "color": pygame.Color("red"), + "rect": pygame.Rect((0, 0), (2, 2)), + "width": 5, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0 + } + + for name in ("rect", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(**invalid_kwargs) + + def test_rect__arg_invalid_types(self): + """Ensures draw rect detects invalid arg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("white") + rect = pygame.Rect((1, 1), (1, 1)) + + with self.assertRaises(TypeError): + # Invalid border_bottom_right_radius. + bounds_rect = self.draw_rect(surface, color, rect, 2, + border_bottom_right_radius="rad") + + with self.assertRaises(TypeError): + # Invalid border_bottom_left_radius. + bounds_rect = self.draw_rect(surface, color, rect, 2, + border_bottom_left_radius="rad") + + with self.assertRaises(TypeError): + # Invalid border_top_right_radius. + bounds_rect = self.draw_rect(surface, color, rect, 2, + border_top_right_radius="rad") + + with self.assertRaises(TypeError): + # Invalid border_top_left_radius. + bounds_rect = self.draw_rect(surface, color, rect, 2, + border_top_left_radius="draw") + + with self.assertRaises(TypeError): + # Invalid border_radius. + bounds_rect = self.draw_rect(surface, color, rect, 2, "rad") + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_rect(surface, color, rect, "2", 4) + + with self.assertRaises(TypeError): + # Invalid rect. + bounds_rect = self.draw_rect(surface, color, (1, 2, 3), 2, 6) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_rect(surface, 2.3, rect, 3, 8) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_rect(rect, color, rect, 4, 10) + + def test_rect__kwarg_invalid_types(self): + """Ensures draw rect detects invalid kwarg types.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("red") + rect = pygame.Rect((0, 0), (1, 1)) + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0 + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0 + }, + { + "surface": surface, + "color": color, + "rect": (1, 1, 2), # Invalid rect. + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0 + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1.1, # Invalid width. + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0 + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10.5, # Invalid border_radius. + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0 + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5.5, # Invalid top_left_radius. + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0 + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": "a", # Invalid top_right_radius. + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0 + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": "c", # Invalid bottom_left_radius + "border_bottom_right_radius": 0 + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": "d" # Invalid bottom_right. + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(**kwargs) + + def test_rect__kwarg_invalid_name(self): + """Ensures draw rect detects invalid kwarg names.""" + surface = pygame.Surface((2, 1)) + color = pygame.Color("green") + rect = pygame.Rect((0, 0), (3, 3)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + "invalid": 1, + }, + {"surface": surface, "color": color, "rect": rect, "invalid": 1}, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(**kwargs) + + def test_rect__args_and_kwargs(self): + """Ensures draw rect accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 1)) + color = (255, 255, 255, 0) + rect = pygame.Rect((1, 0), (2, 5)) + width = 0 + kwargs = {"surface": surface, "color": color, "rect": rect, + "width": width} + + for name in ("surface", "color", "rect", "width"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_rect(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_rect(surface, color, **kwargs) + elif "rect" == name: + bounds_rect = self.draw_rect(surface, color, rect, **kwargs) + else: + bounds_rect = self.draw_rect(surface, color, rect, width, **kwargs) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__valid_width_values(self): + """Ensures draw rect accepts different width values.""" + pos = (1, 1) + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + color = (1, 2, 3, 255) + kwargs = { + "surface": surface, + "color": color, + "rect": pygame.Rect(pos, (2, 2)), + "width": None, + } + + for width in (-1000, -10, -1, 0, 1, 10, 1000): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = color if width >= 0 else surface_color + + bounds_rect = self.draw_rect(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__valid_rect_formats(self): + """Ensures draw rect accepts different rect formats.""" + pos = (1, 1) + expected_color = pygame.Color("yellow") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + kwargs = {"surface": surface, "color": expected_color, "rect": None, "width": 0} + rects = ( + pygame.Rect(pos, (1, 1)), + (pos, (2, 2)), + (pos[0], pos[1], 3, 3), + [pos, (2.1, 2.2)], + ) + + for rect in rects: + surface.fill(surface_color) # Clear for each test. + kwargs["rect"] = rect + + bounds_rect = self.draw_rect(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__invalid_rect_formats(self): + """Ensures draw rect handles invalid rect formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "rect": None, + "width": 0, + } + + invalid_fmts = ( + [], + [1], + [1, 2], + [1, 2, 3], + [1, 2, 3, 4, 5], + set([1, 2, 3, 4]), + [1, 2, 3, "4"], + ) + + for rect in invalid_fmts: + kwargs["rect"] = rect + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(**kwargs) + + def test_rect__valid_color_formats(self): + """Ensures draw rect accepts different color formats.""" + pos = (1, 1) + red_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": None, + "rect": pygame.Rect(pos, (1, 1)), + "width": 3, + } + reds = ((255, 0, 0), (255, 0, 0, 255), surface.map_rgb(red_color), red_color) + + for color in reds: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = red_color + + bounds_rect = self.draw_rect(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__invalid_color_formats(self): + """Ensures draw rect handles invalid color formats correctly.""" + pos = (1, 1) + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": None, + "rect": pygame.Rect(pos, (1, 1)), + "width": 1, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(**kwargs) + + def test_rect__fill(self): + self.surf_w, self.surf_h = self.surf_size = (320, 200) + self.surf = pygame.Surface(self.surf_size, pygame.SRCALPHA) + self.color = (1, 13, 24, 205) + rect = pygame.Rect(10, 10, 25, 20) + drawn = self.draw_rect(self.surf, self.color, rect, 0) + + self.assertEqual(drawn, rect) + + # Should be colored where it's supposed to be + for pt in test_utils.rect_area_pts(rect): + color_at_pt = self.surf.get_at(pt) + + self.assertEqual(color_at_pt, self.color) + + # And not where it shouldn't + for pt in test_utils.rect_outer_bounds(rect): + color_at_pt = self.surf.get_at(pt) + + self.assertNotEqual(color_at_pt, self.color) + + # Issue #310: Cannot draw rectangles that are 1 pixel high + bgcolor = pygame.Color("black") + self.surf.fill(bgcolor) + hrect = pygame.Rect(1, 1, self.surf_w - 2, 1) + vrect = pygame.Rect(1, 3, 1, self.surf_h - 4) + + drawn = self.draw_rect(self.surf, self.color, hrect, 0) + + self.assertEqual(drawn, hrect) + + x, y = hrect.topleft + w, h = hrect.size + + self.assertEqual(self.surf.get_at((x - 1, y)), bgcolor) + self.assertEqual(self.surf.get_at((x + w, y)), bgcolor) + for i in range(x, x + w): + self.assertEqual(self.surf.get_at((i, y)), self.color) + + drawn = self.draw_rect(self.surf, self.color, vrect, 0) + + self.assertEqual(drawn, vrect) + + x, y = vrect.topleft + w, h = vrect.size + + self.assertEqual(self.surf.get_at((x, y - 1)), bgcolor) + self.assertEqual(self.surf.get_at((x, y + h)), bgcolor) + for i in range(y, y + h): + self.assertEqual(self.surf.get_at((x, i)), self.color) + + def test_rect__one_pixel_lines(self): + self.surf = pygame.Surface((320, 200), pygame.SRCALPHA) + self.color = (1, 13, 24, 205) + + rect = pygame.Rect(10, 10, 56, 20) + + drawn = self.draw_rect(self.surf, self.color, rect, 1) + + self.assertEqual(drawn, rect) + + # Should be colored where it's supposed to be + for pt in test_utils.rect_perimeter_pts(drawn): + color_at_pt = self.surf.get_at(pt) + + self.assertEqual(color_at_pt, self.color) + + # And not where it shouldn't + for pt in test_utils.rect_outer_bounds(drawn): + color_at_pt = self.surf.get_at(pt) + + self.assertNotEqual(color_at_pt, self.color) + + def test_rect__bounding_rect(self): + """Ensures draw rect returns the correct bounding rect. + + Tests rects on and off the surface and a range of width/thickness + values. + """ + rect_color = pygame.Color("red") + surf_color = pygame.Color("black") + min_width = min_height = 5 + max_width = max_height = 7 + sizes = ((min_width, min_height), (max_width, max_height)) + surface = pygame.Surface((20, 20), 0, 32) + surf_rect = surface.get_rect() + # Make a rect that is bigger than the surface to help test drawing + # rects off and partially off the surface. + big_rect = surf_rect.inflate(min_width * 2 + 1, min_height * 2 + 1) + + for pos in rect_corners_mids_and_center( + surf_rect + ) + rect_corners_mids_and_center(big_rect): + # Each of the rect's position attributes will be set to the pos + # value. + for attr in RECT_POSITION_ATTRIBUTES: + # Test using different rect sizes and thickness values. + for width, height in sizes: + rect = pygame.Rect((0, 0), (width, height)) + setattr(rect, attr, pos) + + for thickness in range(4): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_rect( + surface, rect_color, rect, thickness + ) + + # Calculating the expected_rect after the rect is + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, rect.topleft + ) + + self.assertEqual( + bounding_rect, + expected_rect, + "thickness={}".format(thickness), + ) + + def test_rect__surface_clip(self): + """Ensures draw rect respects a surface's clip area. + + Tests drawing the rect filled and unfilled. + """ + surfw = surfh = 30 + rect_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (8, 10)) + clip_rect.center = surface.get_rect().center + test_rect = clip_rect.copy() # Manages the rect's pos. + + for width in (0, 1): # Filled and unfilled. + # Test centering the rect along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the rect without the + # clip area set. + test_rect.center = center + surface.set_clip(None) + surface.fill(surface_color) + self.draw_rect(surface, rect_color, test_rect, width) + expected_pts = get_color_points(surface, rect_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the rect + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_rect(surface, rect_color, test_rect, width) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the rect_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = rect_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() + + +class DrawRectTest(DrawRectMixin, DrawTestCase): + """Test draw module function rect. + + This class inherits the general tests from DrawRectMixin. It is also the + class to add any draw.rect specific tests to. + """ + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever properly supports drawing rects. +# @unittest.skip('draw_py.draw_rect not supported yet') +# class PythonDrawRectTest(DrawRectMixin, PythonDrawTestCase): +# """Test draw_py module function draw_rect. +# +# This class inherits the general tests from DrawRectMixin. It is also the +# class to add any draw_py.draw_rect specific tests to. +# """ + + +### Circle Testing ############################################################ + + +class DrawCircleMixin(object): + """Mixin tests for drawing circles. + + This class contains all the general circle drawing tests. + """ + + def test_circle__args(self): + """Ensures draw circle accepts the correct args.""" + bounds_rect = self.draw_circle( + pygame.Surface((3, 3)), (0, 10, 0, 50), (0, 0), 3, 1, 1, 0, 1, 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__args_without_width(self): + """Ensures draw circle accepts the args without a width and + quadrants. """ + bounds_rect = self.draw_circle(pygame.Surface((2, 2)), (0, 0, 0, 50), + (1, 1), 1) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__args_with_negative_width(self): + """Ensures draw circle accepts the args with negative width.""" + bounds_rect = self.draw_circle( + pygame.Surface((2, 2)), (0, 0, 0, 50), (1, 1), 1, -1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + self.assertEqual(bounds_rect, pygame.Rect(1, 1, 0, 0)) + + def test_circle__args_with_width_gt_radius(self): + """Ensures draw circle accepts the args with width > radius.""" + bounds_rect = self.draw_circle( + pygame.Surface((2, 2)), (0, 0, 0, 50), (1, 1), 2, 3, 0, 0, 0, 0 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + self.assertEqual(bounds_rect, pygame.Rect(0, 0, 2, 2)) + + def test_circle__kwargs(self): + """Ensures draw circle accepts the correct kwargs + with and without a width and quadrant arguments. + """ + kwargs_list = [ + { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("yellow"), + "center": (2, 2), + "radius": 2, + "width": 1, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": False, + "draw_bottom_right": True + }, + { + "surface": pygame.Surface((2, 1)), + "color": (0, 10, 20), + "center": (1, 1), + "radius": 1, + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_circle(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__kwargs_order_independent(self): + """Ensures draw circle's kwargs are not order dependent.""" + bounds_rect = self.draw_circle( + draw_top_right=False, + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + width=0, + draw_bottom_left=False, + center=(1, 0), + draw_bottom_right=False, + radius=2, + draw_top_left=True, + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__args_missing(self): + """Ensures draw circle detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(surface, color, (0, 0)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle() + + def test_circle__kwargs_missing(self): + """Ensures draw circle detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((1, 2)), + "color": pygame.Color("red"), + "center": (1, 0), + "radius": 2, + "width": 1, + "draw_top_right": False, + "draw_top_left": False, + "draw_bottom_left": False, + "draw_bottom_right": True + } + + for name in ("radius", "center", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(**invalid_kwargs) + + def test_circle__arg_invalid_types(self): + """Ensures draw circle detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + center = (1, 1) + radius = 1 + + with self.assertRaises(TypeError): + # Invalid draw_top_right. + bounds_rect = self.draw_circle(surface, color, center, radius, 1, + "a", 1, 1, 1) + + with self.assertRaises(TypeError): + # Invalid draw_top_left. + bounds_rect = self.draw_circle(surface, color, center, radius, 1, + 1, "b", 1, 1) + + with self.assertRaises(TypeError): + # Invalid draw_bottom_left. + bounds_rect = self.draw_circle(surface, color, center, radius, 1, + 1, 1, "c", 1) + + with self.assertRaises(TypeError): + # Invalid draw_bottom_right. + bounds_rect = self.draw_circle(surface, color, center, radius, 1, + 1, 1, 1, "d") + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_circle(surface, color, center, radius, "1") + + with self.assertRaises(TypeError): + # Invalid radius. + bounds_rect = self.draw_circle(surface, color, center, "2") + + with self.assertRaises(TypeError): + # Invalid center. + bounds_rect = self.draw_circle(surface, color, (1, 2, 3), radius) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_circle(surface, 2.3, center, radius) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_circle((1, 2, 3, 4), color, center, radius) + + def test_circle__kwarg_invalid_types(self): + """Ensures draw circle detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + center = (0, 1) + radius = 1 + width = 1 + quadrant = 1 + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True + }, + { + "surface": surface, + "color": color, + "center": (1, 1, 1), # Invalid center. + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": "1", # Invalid radius. + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": 1.2, # Invalid width. + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": "True", # Invalid draw_top_right + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": 'True', # Invalid draw_top_left + "draw_bottom_left": True, + "draw_bottom_right": True + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": 3.14, # Invalid draw_bottom_left + "draw_bottom_right": True + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": "quadrant" # Invalid draw_bottom_right + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(**kwargs) + + def test_circle__kwarg_invalid_name(self): + """Ensures draw circle detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + center = (0, 0) + radius = 2 + kwargs_list = [ + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": 1, + "quadrant": 1, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(**kwargs) + + def test_circle__args_and_kwargs(self): + """Ensures draw circle accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 1)) + color = (255, 255, 0, 0) + center = (1, 0) + radius = 2 + width = 0 + draw_top_right = True + draw_top_left = False + draw_bottom_left = False + draw_bottom_right = True + kwargs = { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True + } + + for name in ("surface", "color", "center", "radius", "width", + "draw_top_right", "draw_top_left", "draw_bottom_left", + "draw_bottom_right"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_circle(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_circle(surface, color, **kwargs) + elif "center" == name: + bounds_rect = self.draw_circle(surface, color, center, **kwargs) + elif "radius" == name: + bounds_rect = self.draw_circle(surface, color, center, radius, **kwargs) + elif "width" == name: + bounds_rect = self.draw_circle( + surface, color, center, radius, width, **kwargs + ) + elif "draw_top_right" == name: + bounds_rect = self.draw_circle( + surface, color, center, radius, width, draw_top_right, **kwargs + ) + elif "draw_top_left" == name: + bounds_rect = self.draw_circle( + surface, color, center, radius, width, draw_top_right, draw_top_left, **kwargs + ) + elif "draw_bottom_left" == name: + bounds_rect = self.draw_circle( + surface, color, center, radius, width, draw_top_right, draw_top_left, draw_bottom_left, **kwargs + ) + else: + bounds_rect = self.draw_circle( + surface, color, center, radius, width, draw_top_right, draw_top_left, draw_bottom_left, draw_bottom_right, **kwargs + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__valid_width_values(self): + """Ensures draw circle accepts different width values.""" + center = (2, 2) + radius = 1 + pos = (center[0] - radius, center[1]) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + color = (10, 20, 30, 255) + kwargs = { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": None, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True + } + + for width in (-100, -10, -1, 0, 1, 10, 100): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = color if width >= 0 else surface_color + + bounds_rect = self.draw_circle(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__valid_radius_values(self): + """Ensures draw circle accepts different radius values.""" + pos = center = (2, 2) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + color = (10, 20, 30, 255) + kwargs = { + "surface": surface, + "color": color, + "center": center, + "radius": None, + "width": 0, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True + } + + for radius in (-10, -1, 0, 1, 10): + surface.fill(surface_color) # Clear for each test. + kwargs["radius"] = radius + expected_color = color if radius > 0 else surface_color + + bounds_rect = self.draw_circle(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__valid_center_formats(self): + """Ensures draw circle accepts different center formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "center": None, + "radius": 1, + "width": 0, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True + } + x, y = 2, 2 # center position + + # The center values can be ints or floats. + for center in ((x, y), (x + 0.1, y), (x, y + 0.1), (x + 0.1, y + 0.1)): + # The center type can be a tuple/list/Vector2. + for seq_type in (tuple, list, Vector2): + surface.fill(surface_color) # Clear for each test. + kwargs["center"] = seq_type(center) + + bounds_rect = self.draw_circle(**kwargs) + + self.assertEqual(surface.get_at((x, y)), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__valid_color_formats(self): + """Ensures draw circle accepts different color formats.""" + center = (2, 2) + radius = 1 + pos = (center[0] - radius, center[1]) + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": None, + "center": center, + "radius": radius, + "width": 0, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_circle(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__invalid_color_formats(self): + """Ensures draw circle handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "center": (1, 2), + "radius": 1, + "width": 0, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(**kwargs) + + def test_circle__floats(self): + """Ensure that floats are accepted.""" + draw.circle( + surface=pygame.Surface((4, 4)), + color=(255, 255, 127), + center=(1.5, 1.5), + radius=1.3, + width=0, + draw_top_right=True, + draw_top_left=True, + draw_bottom_left=True, + draw_bottom_right=True + ) + + draw.circle( + surface=pygame.Surface((4, 4)), + color=(255, 255, 127), + center=Vector2(1.5, 1.5), + radius=1.3, + width=0, + draw_top_right=True, + draw_top_left=True, + draw_bottom_left=True, + draw_bottom_right=True + ) + + draw.circle(pygame.Surface((2, 2)), (0, 0, 0, 50), (1.3, 1.3), 1.2) + + # def test_circle_clip(self): + # """ maybe useful to help work out circle clip algorithm.""" + # MAX = max + # MIN = min + # posx=30 + # posy=15 + # radius=1 + # l=29 + # t=14 + # r=30 + # b=16 + # clip_rect_x=0 + # clip_rect_y=0 + # clip_rect_w=30 + # clip_rect_h=30 + + # l = MAX(posx - radius, clip_rect_x) + # t = MAX(posy - radius, clip_rect_y) + # r = MIN(posx + radius, clip_rect_x + clip_rect_w) + # b = MIN(posy + radius, clip_rect_y + clip_rect_h) + + # l, t, MAX(r - l, 0), MAX(b - t, 0) + + def test_circle__bounding_rect(self): + """Ensures draw circle returns the correct bounding rect. + + Tests circles on and off the surface and a range of width/thickness + values. + """ + circle_color = pygame.Color("red") + surf_color = pygame.Color("black") + max_radius = 3 + surface = pygame.Surface((30, 30), 0, 32) + surf_rect = surface.get_rect() + # Make a rect that is bigger than the surface to help test drawing + # circles off and partially off the surface. Make this rect such that + # when centering the test circle on one of its corners, the circle is + # drawn fully off the test surface, but a rect bounding the circle + # would still overlap with the test surface. + big_rect = surf_rect.inflate(max_radius * 2 - 1, max_radius * 2 - 1) + + for pos in rect_corners_mids_and_center( + surf_rect + ) + rect_corners_mids_and_center(big_rect): + # Test using different radius and thickness values. + for radius in range(max_radius + 1): + for thickness in range(radius + 1): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_circle( + surface, circle_color, pos, radius, thickness + ) + + # Calculating the expected_rect after the circle is + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect(surface, surf_color, pos) + # print("pos:%s:, radius:%s:, thickness:%s:" % (pos, radius, thickness)) + self.assertEqual(bounding_rect, expected_rect) + + def test_circle_negative_radius(self): + """ Ensures negative radius circles return zero sized bounding rect. + """ + surf = pygame.Surface((200, 200)) + color = (0, 0, 0, 50) + center = surf.get_height() // 2, surf.get_height() // 2 + + bounding_rect = self.draw_circle(surf, color, center, radius=-1, width=1) + self.assertEqual(bounding_rect.size, (0, 0)) + + def test_circle_zero_radius(self): + """ Ensures zero radius circles does not draw a center pixel. + + NOTE: This is backwards incompatible behaviour with 1.9.x. + """ + surf = pygame.Surface((200, 200)) + circle_color = pygame.Color("red") + surf_color = pygame.Color("black") + surf.fill((0, 0, 0)) + center = (100, 100) + radius = 0 + width = 1 + + bounding_rect = self.draw_circle(surf, circle_color, center, radius, width) + expected_rect = create_bounding_rect(surf, surf_color, center) + self.assertEqual(bounding_rect, expected_rect) + self.assertEqual(bounding_rect, pygame.Rect(100, 100, 0, 0)) + + def test_circle__surface_clip(self): + """Ensures draw circle respects a surface's clip area. + + Tests drawing the circle filled and unfilled. + """ + surfw = surfh = 25 + circle_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (10, 10)) + clip_rect.center = surface.get_rect().center + radius = clip_rect.w // 2 + 1 + + for width in (0, 1): # Filled and unfilled. + # Test centering the circle along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the circle without the + # clip area set. + surface.set_clip(None) + surface.fill(surface_color) + self.draw_circle(surface, circle_color, center, radius, width) + expected_pts = get_color_points(surface, circle_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the circle + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_circle(surface, circle_color, center, radius, width) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the circle_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = circle_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() + + def test_circle_shape(self): + """Ensures there are no holes in the circle, and no overdrawing. + + Tests drawing a thick circle. + Measures the distance of the drawn pixels from the circle center. + """ + surfw = surfh = 100 + circle_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + (cx, cy) = center = (50, 50) + radius = 45 + width = 25 + + dest_rect = self.draw_circle(surface, circle_color, center, radius, width) + + for pt in test_utils.rect_area_pts(dest_rect): + x, y = pt + sqr_distance = (x - cx) ** 2 + (y - cy) ** 2 + if (radius - width + 1) ** 2 < sqr_distance < (radius - 1) ** 2: + self.assertEqual(surface.get_at(pt), circle_color) + if ( + sqr_distance < (radius - width - 1) ** 2 + or sqr_distance > (radius + 1) ** 2 + ): + self.assertEqual(surface.get_at(pt), surface_color) + + def test_circle__diameter(self): + """ Ensures draw circle is twice size of radius high and wide.""" + surf = pygame.Surface((200, 200)) + color = (0, 0, 0, 50) + center = surf.get_height() // 2, surf.get_height() // 2 + width = 1 + radius = 6 + for radius in range(1, 65): + bounding_rect = self.draw_circle(surf, color, center, radius, width) + self.assertEqual(bounding_rect.width, radius * 2) + self.assertEqual(bounding_rect.height, radius * 2) + + +class DrawCircleTest(DrawCircleMixin, DrawTestCase): + """Test draw module function circle. + + This class inherits the general tests from DrawCircleMixin. It is also + the class to add any draw.circle specific tests to. + """ + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever properly supports drawing circles. +# @unittest.skip('draw_py.draw_circle not supported yet') +# class PythonDrawCircleTest(DrawCircleMixin, PythonDrawTestCase): +# """Test draw_py module function draw_circle." +# +# This class inherits the general tests from DrawCircleMixin. It is also +# the class to add any draw_py.draw_circle specific tests to. +# """ + + +### Arc Testing ############################################################### + + +class DrawArcMixin(object): + """Mixin tests for drawing arcs. + + This class contains all the general arc drawing tests. + """ + + def test_arc__args(self): + """Ensures draw arc accepts the correct args.""" + bounds_rect = self.draw_arc( + pygame.Surface((3, 3)), (0, 10, 0, 50), (1, 1, 2, 2), 0, 1, 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__args_without_width(self): + """Ensures draw arc accepts the args without a width.""" + bounds_rect = self.draw_arc( + pygame.Surface((2, 2)), (1, 1, 1, 99), pygame.Rect((0, 0), (2, 2)), 1.1, 2.1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__args_with_negative_width(self): + """Ensures draw arc accepts the args with negative width.""" + bounds_rect = self.draw_arc( + pygame.Surface((3, 3)), (10, 10, 50, 50), (1, 1, 2, 2), 0, 1, -1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + self.assertEqual(bounds_rect, pygame.Rect(1, 1, 0, 0)) + + def test_arc__args_with_width_gt_radius(self): + """Ensures draw arc accepts the args with + width > rect.w // 2 and width > rect.h // 2. + """ + rect = pygame.Rect((0, 0), (4, 4)) + bounds_rect = self.draw_arc( + pygame.Surface((3, 3)), (10, 10, 50, 50), rect, 0, 45, rect.w // 2 + 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + bounds_rect = self.draw_arc( + pygame.Surface((3, 3)), (10, 10, 50, 50), rect, 0, 45, rect.h // 2 + 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__kwargs(self): + """Ensures draw arc accepts the correct kwargs + with and without a width arg. + """ + kwargs_list = [ + { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("yellow"), + "rect": pygame.Rect((0, 0), (3, 2)), + "start_angle": 0.5, + "stop_angle": 3, + "width": 1, + }, + { + "surface": pygame.Surface((2, 1)), + "color": (0, 10, 20), + "rect": (0, 0, 2, 2), + "start_angle": 1, + "stop_angle": 3.1, + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_arc(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__kwargs_order_independent(self): + """Ensures draw arc's kwargs are not order dependent.""" + bounds_rect = self.draw_arc( + stop_angle=1, + start_angle=2.2, + color=(1, 2, 3), + surface=pygame.Surface((3, 2)), + width=1, + rect=pygame.Rect((1, 0), (2, 3)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__args_missing(self): + """Ensures draw arc detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("red") + rect = pygame.Rect((0, 0), (2, 2)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(surface, color, rect, 0.1) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(surface, color, rect) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc() + + def test_arc__kwargs_missing(self): + """Ensures draw arc detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((1, 2)), + "color": pygame.Color("red"), + "rect": pygame.Rect((1, 0), (2, 2)), + "start_angle": 0.1, + "stop_angle": 2, + "width": 1, + } + + for name in ("stop_angle", "start_angle", "rect", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(**invalid_kwargs) + + def test_arc__arg_invalid_types(self): + """Ensures draw arc detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + rect = pygame.Rect((1, 1), (3, 3)) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_arc(surface, color, rect, 0, 1, "1") + + with self.assertRaises(TypeError): + # Invalid stop_angle. + bounds_rect = self.draw_arc(surface, color, rect, 0, "1", 1) + + with self.assertRaises(TypeError): + # Invalid start_angle. + bounds_rect = self.draw_arc(surface, color, rect, "1", 0, 1) + + with self.assertRaises(TypeError): + # Invalid rect. + bounds_rect = self.draw_arc(surface, color, (1, 2, 3, 4, 5), 0, 1, 1) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_arc(surface, 2.3, rect, 0, 1, 1) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_arc(rect, color, rect, 0, 1, 1) + + def test_arc__kwarg_invalid_types(self): + """Ensures draw arc detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + rect = pygame.Rect((0, 1), (4, 2)) + start = 3 + stop = 4 + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "width": 1, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "width": 1, + }, + { + "surface": surface, + "color": color, + "rect": (0, 0, 0), # Invalid rect. + "start_angle": start, + "stop_angle": stop, + "width": 1, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": "1", # Invalid start_angle. + "stop_angle": stop, + "width": 1, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": "1", # Invalid stop_angle. + "width": 1, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "width": 1.1, + }, + ] # Invalid width. + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(**kwargs) + + def test_arc__kwarg_invalid_name(self): + """Ensures draw arc detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + rect = pygame.Rect((0, 1), (2, 2)) + start = 0.9 + stop = 2.3 + kwargs_list = [ + { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "width": 1, + "invalid": 1, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(**kwargs) + + def test_arc__args_and_kwargs(self): + """Ensures draw arc accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 1)) + color = (255, 255, 0, 0) + rect = pygame.Rect((1, 0), (2, 3)) + start = 0.6 + stop = 2 + width = 1 + kwargs = { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "width": width, + } + + for name in ("surface", "color", "rect", "start_angle", "stop_angle"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_arc(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_arc(surface, color, **kwargs) + elif "rect" == name: + bounds_rect = self.draw_arc(surface, color, rect, **kwargs) + elif "start_angle" == name: + bounds_rect = self.draw_arc(surface, color, rect, start, **kwargs) + elif "stop_angle" == name: + bounds_rect = self.draw_arc(surface, color, rect, start, stop, **kwargs) + else: + bounds_rect = self.draw_arc( + surface, color, rect, start, stop, width, **kwargs + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__valid_width_values(self): + """Ensures draw arc accepts different width values.""" + arc_color = pygame.Color("yellow") + surface_color = pygame.Color("white") + surface = pygame.Surface((6, 6)) + rect = pygame.Rect((0, 0), (4, 4)) + rect.center = surface.get_rect().center + pos = rect.centerx + 1, rect.centery + 1 + kwargs = { + "surface": surface, + "color": arc_color, + "rect": rect, + "start_angle": 0, + "stop_angle": 7, + "width": None, + } + + for width in (-50, -10, -3, -2, -1, 0, 1, 2, 3, 10, 50): + msg = "width={}".format(width) + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = arc_color if width > 0 else surface_color + + bounds_rect = self.draw_arc(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color, msg) + self.assertIsInstance(bounds_rect, pygame.Rect, msg) + + def test_arc__valid_stop_angle_values(self): + """Ensures draw arc accepts different stop_angle values.""" + expected_color = pygame.Color("blue") + surface_color = pygame.Color("white") + surface = pygame.Surface((6, 6)) + rect = pygame.Rect((0, 0), (4, 4)) + rect.center = surface.get_rect().center + pos = rect.centerx, rect.centery + 1 + kwargs = { + "surface": surface, + "color": expected_color, + "rect": rect, + "start_angle": -17, + "stop_angle": None, + "width": 1, + } + + for stop_angle in (-10, -5.5, -1, 0, 1, 5.5, 10): + msg = "stop_angle={}".format(stop_angle) + surface.fill(surface_color) # Clear for each test. + kwargs["stop_angle"] = stop_angle + + bounds_rect = self.draw_arc(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color, msg) + self.assertIsInstance(bounds_rect, pygame.Rect, msg) + + def test_arc__valid_start_angle_values(self): + """Ensures draw arc accepts different start_angle values.""" + expected_color = pygame.Color("blue") + surface_color = pygame.Color("white") + surface = pygame.Surface((6, 6)) + rect = pygame.Rect((0, 0), (4, 4)) + rect.center = surface.get_rect().center + pos = rect.centerx + 1, rect.centery + 1 + kwargs = { + "surface": surface, + "color": expected_color, + "rect": rect, + "start_angle": None, + "stop_angle": 17, + "width": 1, + } + + for start_angle in (-10.0, -5.5, -1, 0, 1, 5.5, 10.0): + msg = "start_angle={}".format(start_angle) + surface.fill(surface_color) # Clear for each test. + kwargs["start_angle"] = start_angle + + bounds_rect = self.draw_arc(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color, msg) + self.assertIsInstance(bounds_rect, pygame.Rect, msg) + + def test_arc__valid_rect_formats(self): + """Ensures draw arc accepts different rect formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((6, 6)) + rect = pygame.Rect((0, 0), (4, 4)) + rect.center = surface.get_rect().center + pos = rect.centerx + 1, rect.centery + 1 + kwargs = { + "surface": surface, + "color": expected_color, + "rect": None, + "start_angle": 0, + "stop_angle": 7, + "width": 1, + } + rects = (rect, (rect.topleft, rect.size), (rect.x, rect.y, rect.w, rect.h)) + + for rect in rects: + surface.fill(surface_color) # Clear for each test. + kwargs["rect"] = rect + + bounds_rect = self.draw_arc(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__valid_color_formats(self): + """Ensures draw arc accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((6, 6)) + rect = pygame.Rect((0, 0), (4, 4)) + rect.center = surface.get_rect().center + pos = rect.centerx + 1, rect.centery + 1 + kwargs = { + "surface": surface, + "color": None, + "rect": rect, + "start_angle": 0, + "stop_angle": 7, + "width": 1, + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_arc(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__invalid_color_formats(self): + """Ensures draw arc handles invalid color formats correctly.""" + pos = (1, 1) + surface = pygame.Surface((4, 3)) + kwargs = { + "surface": surface, + "color": None, + "rect": pygame.Rect(pos, (2, 2)), + "start_angle": 5, + "stop_angle": 6.1, + "width": 1, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(**kwargs) + + def todo_test_arc(self): + """Ensure draw arc works correctly.""" + self.fail() + + def test_arc__bounding_rect(self): + """Ensures draw arc returns the correct bounding rect. + + Tests arcs on and off the surface and a range of width/thickness + values. + """ + arc_color = pygame.Color("red") + surf_color = pygame.Color("black") + min_width = min_height = 5 + max_width = max_height = 7 + sizes = ((min_width, min_height), (max_width, max_height)) + surface = pygame.Surface((20, 20), 0, 32) + surf_rect = surface.get_rect() + # Make a rect that is bigger than the surface to help test drawing + # arcs off and partially off the surface. + big_rect = surf_rect.inflate(min_width * 2 + 1, min_height * 2 + 1) + + # Max angle allows for a full circle to be drawn. + start_angle = 0 + stop_angles = (0, 2, 3, 5, math.ceil(2 * math.pi)) + + for pos in rect_corners_mids_and_center( + surf_rect + ) + rect_corners_mids_and_center(big_rect): + # Each of the arc's rect position attributes will be set to the pos + # value. + for attr in RECT_POSITION_ATTRIBUTES: + # Test using different rect sizes, thickness values and stop + # angles. + for width, height in sizes: + arc_rect = pygame.Rect((0, 0), (width, height)) + setattr(arc_rect, attr, pos) + + for thickness in (0, 1, 2, 3, min(width, height)): + for stop_angle in stop_angles: + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_arc( + surface, + arc_color, + arc_rect, + start_angle, + stop_angle, + thickness, + ) + + # Calculating the expected_rect after the arc + # is drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, arc_rect.topleft + ) + + self.assertEqual( + bounding_rect, + expected_rect, + "thickness={}".format(thickness), + ) + + def test_arc__surface_clip(self): + """Ensures draw arc respects a surface's clip area.""" + surfw = surfh = 30 + start = 0.1 + end = 0 # end < start so a full circle will be drawn + arc_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the arc's pos. + + for thickness in (1, 3): # Different line widths. + # Test centering the arc along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the arc without the + # clip area set. + pos_rect.center = center + surface.set_clip(None) + surface.fill(surface_color) + self.draw_arc(surface, arc_color, pos_rect, start, end, thickness) + expected_pts = get_color_points(surface, arc_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the arc + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_arc(surface, arc_color, pos_rect, start, end, thickness) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the arc_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = arc_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() + + +class DrawArcTest(DrawArcMixin, DrawTestCase): + """Test draw module function arc. + + This class inherits the general tests from DrawArcMixin. It is also the + class to add any draw.arc specific tests to. + """ + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever properly supports drawing arcs. +# @unittest.skip('draw_py.draw_arc not supported yet') +# class PythonDrawArcTest(DrawArcMixin, PythonDrawTestCase): +# """Test draw_py module function draw_arc. +# +# This class inherits the general tests from DrawArcMixin. It is also the +# class to add any draw_py.draw_arc specific tests to. +# """ + + +### Draw Module Testing ####################################################### + + +class DrawModuleTest(unittest.TestCase): + """General draw module tests.""" + + def test_path_data_validation(self): + """Test validation of multi-point drawing methods. + + See bug #521 + """ + surf = pygame.Surface((5, 5)) + rect = pygame.Rect(0, 0, 5, 5) + bad_values = ( + "text", + b"bytes", + 1 + 1j, # string, bytes, complex, + object(), + (lambda x: x), + ) # object, function + bad_points = list(bad_values) + [(1,), (1, 2, 3)] # wrong tuple length + bad_points.extend((1, v) for v in bad_values) # one wrong value + good_path = [(1, 1), (1, 3), (3, 3), (3, 1)] + # A) draw.lines + check_pts = [(x, y) for x in range(5) for y in range(5)] + + for method, is_polgon in ( + (draw.lines, 0), + (draw.aalines, 0), + (draw.polygon, 1), + ): + for val in bad_values: + # 1. at the beginning + draw.rect(surf, RED, rect, 0) + with self.assertRaises(TypeError): + if is_polgon: + method(surf, GREEN, [val] + good_path, 0) + else: + method(surf, GREEN, True, [val] + good_path) + + # make sure, nothing was drawn : + self.assertTrue(all(surf.get_at(pt) == RED for pt in check_pts)) + + # 2. not at the beginning (was not checked) + draw.rect(surf, RED, rect, 0) + with self.assertRaises(TypeError): + path = good_path[:2] + [val] + good_path[2:] + if is_polgon: + method(surf, GREEN, path, 0) + else: + method(surf, GREEN, True, path) + + # make sure, nothing was drawn : + self.assertTrue(all(surf.get_at(pt) == RED for pt in check_pts)) + + def test_color_validation(self): + surf = pygame.Surface((10, 10)) + colors = 123456, (1, 10, 100), RED, '#ab12df', 'red' + points = ((0, 0), (1, 1), (1, 0)) + + # 1. valid colors + for col in colors: + draw.line(surf, col, (0, 0), (1, 1)) + draw.aaline(surf, col, (0, 0), (1, 1)) + draw.aalines(surf, col, True, points) + draw.lines(surf, col, True, points) + draw.arc(surf, col, pygame.Rect(0, 0, 3, 3), 15, 150) + draw.ellipse(surf, col, pygame.Rect(0, 0, 3, 6), 1) + draw.circle(surf, col, (7, 3), 2) + draw.polygon(surf, col, points, 0) + + # 2. invalid colors + for col in (1.256, object(), None): + with self.assertRaises(TypeError): + draw.line(surf, col, (0, 0), (1, 1)) + + with self.assertRaises(TypeError): + draw.aaline(surf, col, (0, 0), (1, 1)) + + with self.assertRaises(TypeError): + draw.aalines(surf, col, True, points) + + with self.assertRaises(TypeError): + draw.lines(surf, col, True, points) + + with self.assertRaises(TypeError): + draw.arc(surf, col, pygame.Rect(0, 0, 3, 3), 15, 150) + + with self.assertRaises(TypeError): + draw.ellipse(surf, col, pygame.Rect(0, 0, 3, 6), 1) + + with self.assertRaises(TypeError): + draw.circle(surf, col, (7, 3), 2) + + with self.assertRaises(TypeError): + draw.polygon(surf, col, points, 0) + + +############################################################################### + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/event_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/event_test.py new file mode 100644 index 0000000..af38354 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/event_test.py @@ -0,0 +1,679 @@ +import os +import sys +import unittest + +import pygame +from pygame.compat import as_unicode + + +PY3 = sys.version_info >= (3, 0, 0) + + +################################################################################ + +EVENT_TYPES = ( + # pygame.NOEVENT, + # pygame.ACTIVEEVENT, + pygame.KEYDOWN, + pygame.KEYUP, + pygame.MOUSEMOTION, + pygame.MOUSEBUTTONDOWN, + pygame.MOUSEBUTTONUP, + pygame.JOYAXISMOTION, + pygame.JOYBALLMOTION, + pygame.JOYHATMOTION, + pygame.JOYBUTTONDOWN, + pygame.JOYBUTTONUP, + pygame.VIDEORESIZE, + pygame.VIDEOEXPOSE, + pygame.QUIT, + pygame.SYSWMEVENT, + pygame.USEREVENT, + # pygame.NUMEVENTS, +) + + +NAMES_AND_EVENTS = ( + ("NoEvent", pygame.NOEVENT), + ("ActiveEvent", pygame.ACTIVEEVENT), + ("KeyDown", pygame.KEYDOWN), + ("KeyUp", pygame.KEYUP), + ("MouseMotion", pygame.MOUSEMOTION), + ("MouseButtonDown", pygame.MOUSEBUTTONDOWN), + ("MouseButtonUp", pygame.MOUSEBUTTONUP), + ("JoyAxisMotion", pygame.JOYAXISMOTION), + ("JoyBallMotion", pygame.JOYBALLMOTION), + ("JoyHatMotion", pygame.JOYHATMOTION), + ("JoyButtonDown", pygame.JOYBUTTONDOWN), + ("JoyButtonUp", pygame.JOYBUTTONUP), + ("VideoResize", pygame.VIDEORESIZE), + ("VideoExpose", pygame.VIDEOEXPOSE), + ("Quit", pygame.QUIT), + ("SysWMEvent", pygame.SYSWMEVENT), + ("MidiIn", pygame.MIDIIN), + ("MidiOut", pygame.MIDIOUT), + ("UserEvent", pygame.USEREVENT), + ("Unknown", 0xFFFF), +) + +# Add in any SDL 2 specific events. +if pygame.get_sdl_version()[0] >= 2: + NAMES_AND_EVENTS += ( + ("FingerMotion", pygame.FINGERMOTION), + ("FingerDown", pygame.FINGERDOWN), + ("FingerUp", pygame.FINGERUP), + ("MultiGesture", pygame.MULTIGESTURE), + ("MouseWheel", pygame.MOUSEWHEEL), + ("TextInput", pygame.TEXTINPUT), + ("TextEditing", pygame.TEXTEDITING), + ("WindowEvent", pygame.WINDOWEVENT), + ("ControllerAxisMotion", pygame.CONTROLLERAXISMOTION), + ("ControllerButtonDown", pygame.CONTROLLERBUTTONDOWN), + ("ControllerButtonUp", pygame.CONTROLLERBUTTONUP), + ("ControllerDeviceAdded", pygame.CONTROLLERDEVICEADDED), + ("ControllerDeviceRemoved", pygame.CONTROLLERDEVICEREMOVED), + ("ControllerDeviceMapped", pygame.CONTROLLERDEVICEREMAPPED), + ("DropFile", pygame.DROPFILE), + ) + + # Add in any SDL 2.0.4 specific events. + if pygame.get_sdl_version() >= (2, 0, 4): + NAMES_AND_EVENTS += ( + ("AudioDeviceAdded", pygame.AUDIODEVICEADDED), + ("AudioDeviceRemoved", pygame.AUDIODEVICEREMOVED), + ) + + # Add in any SDL 2.0.5 specific events. + if pygame.get_sdl_version() >= (2, 0, 5): + NAMES_AND_EVENTS += ( + ("DropText", pygame.DROPTEXT), + ("DropBegin", pygame.DROPBEGIN), + ("DropComplete", pygame.DROPCOMPLETE), + ) + + +class EventTypeTest(unittest.TestCase): + def test_Event(self): + """Ensure an Event object can be created.""" + e = pygame.event.Event(pygame.USEREVENT, some_attr=1, other_attr="1") + + self.assertEqual(e.some_attr, 1) + self.assertEqual(e.other_attr, "1") + + # Event now uses tp_dictoffset and tp_members: request 62 + # on Motherhamster Bugzilla. + self.assertEqual(e.type, pygame.USEREVENT) + self.assertIs(e.dict, e.__dict__) + + e.some_attr = 12 + + self.assertEqual(e.some_attr, 12) + + e.new_attr = 15 + + self.assertEqual(e.new_attr, 15) + + # For Python 2.x a TypeError is raised for a readonly member; + # for Python 3.x it is an AttributeError. + self.assertRaises((TypeError, AttributeError), setattr, e, "type", 0) + self.assertRaises((TypeError, AttributeError), setattr, e, "dict", None) + + # Ensure attributes are visible to dir(), part of the original + # posted request. + d = dir(e) + attrs = ("type", "dict", "__dict__", "some_attr", "other_attr", "new_attr") + + for attr in attrs: + self.assertIn(attr, d) + + def test_as_str(self): + # Bug reported on Pygame mailing list July 24, 2011: + # For Python 3.x str(event) to raises an UnicodeEncodeError when + # an event attribute is a string with a non-ascii character. + try: + str(pygame.event.Event(EVENT_TYPES[0], a=as_unicode(r"\xed"))) + except UnicodeEncodeError: + self.fail("Event object raised exception for non-ascii character") + # Passed. + + +race_condition_notification = """ +This test is dependent on timing. The event queue is cleared in preparation for +tests. There is a small window where outside events from the OS may have effected +results. Try running the test again. +""" + + +class EventModuleArgsTest(unittest.TestCase): + def setUp(self): + pygame.display.init() + pygame.event.clear() + + def tearDown(self): + pygame.display.quit() + + def test_get(self): + pygame.event.get() + pygame.event.get(None) + pygame.event.get(None, True) + + pygame.event.get(pump=False) + pygame.event.get(pump=True) + pygame.event.get(eventtype=None) + pygame.event.get(eventtype=[pygame.KEYUP, pygame.KEYDOWN]) + pygame.event.get(eventtype=pygame.USEREVENT, pump=False) + + def test_clear(self): + pygame.event.clear() + pygame.event.clear(None) + pygame.event.clear(None, True) + + pygame.event.clear(pump=False) + pygame.event.clear(pump=True) + pygame.event.clear(eventtype=None) + pygame.event.clear(eventtype=[pygame.KEYUP, pygame.KEYDOWN]) + pygame.event.clear(eventtype=pygame.USEREVENT, pump=False) + + def test_peek(self): + pygame.event.peek() + pygame.event.peek(None) + pygame.event.peek(None, True) + + pygame.event.peek(pump=False) + pygame.event.peek(pump=True) + pygame.event.peek(eventtype=None) + pygame.event.peek(eventtype=[pygame.KEYUP, pygame.KEYDOWN]) + pygame.event.peek(eventtype=pygame.USEREVENT, pump=False) + + +class EventModuleTest(unittest.TestCase): + def _assertCountEqual(self, *args, **kwargs): + # Handle method name differences between Python versions. + if PY3: + self.assertCountEqual(*args, **kwargs) + else: + self.assertItemsEqual(*args, **kwargs) + + def setUp(self): + pygame.display.init() + pygame.event.clear() # flush events + + def tearDown(self): + pygame.event.clear() # flush events + pygame.display.quit() + + def test_event_numevents(self): + """Ensures NUMEVENTS does not exceed the maximum SDL number of events. + """ + # Ref: https://www.libsdl.org/tmp/SDL/include/SDL_events.h + MAX_SDL_EVENTS = 0xFFFF + 1 # SDL_LASTEVENT = 0xFFFF + + self.assertLessEqual(pygame.NUMEVENTS, MAX_SDL_EVENTS) + + def test_event_attribute(self): + e1 = pygame.event.Event(pygame.USEREVENT, attr1="attr1") + self.assertEqual(e1.attr1, "attr1") + + def test_set_blocked(self): + """Ensure events can be blocked from the queue.""" + event = EVENT_TYPES[0] + pygame.event.set_blocked(event) + + self.assertTrue(pygame.event.get_blocked(event)) + + pygame.event.post(pygame.event.Event(event)) + ret = pygame.event.get() + should_be_blocked = [e for e in ret if e.type == event] + + self.assertEqual(should_be_blocked, []) + + def test_set_blocked__event_sequence(self): + """Ensure a sequence of event types can be blocked.""" + event_types = [ + pygame.KEYDOWN, + pygame.KEYUP, + pygame.MOUSEMOTION, + pygame.MOUSEBUTTONDOWN, + pygame.MOUSEBUTTONUP, + ] + + pygame.event.set_blocked(event_types) + + for etype in event_types: + self.assertTrue(pygame.event.get_blocked(etype)) + + def test_set_blocked_all(self): + """Ensure all events can be unblocked at once.""" + pygame.event.set_blocked(None) + + for e in EVENT_TYPES: + self.assertTrue(pygame.event.get_blocked(e)) + + def test_post__and_poll(self): + """Ensure events can be posted to the queue.""" + e1 = pygame.event.Event(pygame.USEREVENT, attr1="attr1") + pygame.event.post(e1) + posted_event = pygame.event.poll() + + self.assertEqual(e1.attr1, posted_event.attr1, race_condition_notification) + + # fuzzing event types + for i in range(1, 11): + pygame.event.post(pygame.event.Event(EVENT_TYPES[i])) + + self.assertEqual( + pygame.event.poll().type, EVENT_TYPES[i], race_condition_notification + ) + + def test_post_large_user_event(self): + pygame.event.post(pygame.event.Event(pygame.USEREVENT, {"a": "a" * 1024})) + e = pygame.event.poll() + + self.assertEqual(e.type, pygame.USEREVENT) + self.assertEqual(e.a, "a" * 1024) + + def test_get(self): + """Ensure get() retrieves all the events on the queue.""" + event_cnt = 10 + for _ in range(event_cnt): + pygame.event.post(pygame.event.Event(pygame.USEREVENT)) + + queue = pygame.event.get() + + self.assertEqual(len(queue), event_cnt) + self.assertTrue(all(e.type == pygame.USEREVENT for e in queue)) + + def test_get_type(self): + ev = pygame.event.Event(pygame.USEREVENT) + pygame.event.post(ev) + queue = pygame.event.get(pygame.USEREVENT) + self.assertEqual(len(queue), 1) + self.assertEqual(queue[0].type, pygame.USEREVENT) + + def test_get__empty_queue(self): + """Ensure get() works correctly on an empty queue.""" + expected_events = [] + pygame.event.clear() + + # Ensure all events can be checked. + retrieved_events = pygame.event.get() + + self.assertListEqual(retrieved_events, expected_events) + + # Ensure events can be checked individually. + for event_type in EVENT_TYPES: + retrieved_events = pygame.event.get(event_type) + + self.assertListEqual(retrieved_events, expected_events) + + # Ensure events can be checked as a sequence. + retrieved_events = pygame.event.get(EVENT_TYPES) + + self.assertListEqual(retrieved_events, expected_events) + + def test_get__event_sequence(self): + """Ensure get() can handle a sequence of event types.""" + event_types = [pygame.KEYDOWN, pygame.KEYUP, pygame.MOUSEMOTION] + other_event_type = pygame.MOUSEBUTTONUP + + # Test when no events in the queue. + expected_events = [] + pygame.event.clear() + retrieved_events = pygame.event.get(event_types) + + self._assertCountEqual(retrieved_events, expected_events) + + # Test when an event type not in the list is in the queue. + expected_events = [] + pygame.event.clear() + pygame.event.post(pygame.event.Event(other_event_type)) + + retrieved_events = pygame.event.get(event_types) + + self._assertCountEqual(retrieved_events, expected_events) + + # Test when 1 event type in the list is in the queue. + expected_events = [pygame.event.Event(event_types[0])] + pygame.event.clear() + pygame.event.post(expected_events[0]) + + retrieved_events = pygame.event.get(event_types) + + self._assertCountEqual(retrieved_events, expected_events) + + # Test all events in the list are in the queue. + pygame.event.clear() + expected_events = [] + + for etype in event_types: + expected_events.append(pygame.event.Event(etype)) + pygame.event.post(expected_events[-1]) + + retrieved_events = pygame.event.get(event_types) + + self._assertCountEqual(retrieved_events, expected_events) + + def test_clear(self): + """Ensure clear() removes all the events on the queue.""" + for e in EVENT_TYPES: + pygame.event.post(pygame.event.Event(e)) + + poll_event = pygame.event.poll() + + self.assertNotEqual(poll_event.type, pygame.NOEVENT) + + pygame.event.clear() + poll_event = pygame.event.poll() + + self.assertEqual(poll_event.type, pygame.NOEVENT, race_condition_notification) + + def test_clear__empty_queue(self): + """Ensure clear() works correctly on an empty queue.""" + expected_events = [] + pygame.event.clear() + + # Test calling clear() on an already empty queue. + pygame.event.clear() + + retrieved_events = pygame.event.get() + + self.assertListEqual(retrieved_events, expected_events) + + def test_clear__event_sequence(self): + """Ensure a sequence of event types can be cleared from the queue.""" + cleared_event_types = EVENT_TYPES[:5] + expected_event_types = EVENT_TYPES[5:10] + expected_events = [] + + # Add the events to the queue. + for etype in cleared_event_types: + pygame.event.post(pygame.event.Event(etype)) + + for etype in expected_events: + expected_events.append(pygame.event.Event(etype)) + pygame.event.post(expected_events[-1]) + + # Clear the cleared_events from the queue. + pygame.event.clear(cleared_event_types) + + # Check the rest of the events in the queue. + remaining_events = pygame.event.get() + + self._assertCountEqual(remaining_events, expected_events) + + def test_event_name(self): + """Ensure event_name() returns the correct event name.""" + for expected_name, event in NAMES_AND_EVENTS: + self.assertEqual( + pygame.event.event_name(event), expected_name, "0x{:X}".format(event) + ) + + def test_event_name__userevent_range(self): + """Ensures event_name() returns the correct name for user events. + + Tests the full range of user events. + """ + expected_name = "UserEvent" + + for event in range(pygame.USEREVENT, pygame.NUMEVENTS): + self.assertEqual( + pygame.event.event_name(event), expected_name, "0x{:X}".format(event) + ) + + def test_event_name__userevent_boundary(self): + """Ensures event_name() does not return 'UserEvent' for events + just outside the user event range. + """ + unexpected_name = "UserEvent" + + for event in (pygame.USEREVENT - 1, pygame.NUMEVENTS): + self.assertNotEqual( + pygame.event.event_name(event), unexpected_name, "0x{:X}".format(event) + ) + + def test_wait(self): + """Ensure wait() waits for an event on the queue.""" + event = pygame.event.Event(EVENT_TYPES[0]) + pygame.event.post(event) + wait_event = pygame.event.wait() + + self.assertEqual(wait_event.type, event.type) + + def test_peek(self): + """Ensure queued events can be peeked at.""" + event_types = [pygame.KEYDOWN, pygame.KEYUP, pygame.MOUSEMOTION] + + for event_type in event_types: + pygame.event.post(pygame.event.Event(event_type)) + + # Ensure events can be checked individually. + for event_type in event_types: + self.assertTrue(pygame.event.peek(event_type)) + + # Ensure events can be checked as a sequence. + self.assertTrue(pygame.event.peek(event_types)) + + def test_peek__event_sequence(self): + """Ensure peek() can handle a sequence of event types.""" + event_types = [pygame.KEYDOWN, pygame.KEYUP, pygame.MOUSEMOTION] + other_event_type = pygame.MOUSEBUTTONUP + + # Test when no events in the queue. + pygame.event.clear() + peeked = pygame.event.peek(event_types) + + self.assertFalse(peeked) + + # Test when an event type not in the list is in the queue. + pygame.event.clear() + pygame.event.post(pygame.event.Event(other_event_type)) + + peeked = pygame.event.peek(event_types) + + self.assertFalse(peeked) + + # Test when 1 event type in the list is in the queue. + pygame.event.clear() + pygame.event.post(pygame.event.Event(event_types[0])) + + peeked = pygame.event.peek(event_types) + + self.assertTrue(peeked) + + # Test all events in the list are in the queue. + pygame.event.clear() + for etype in event_types: + pygame.event.post(pygame.event.Event(etype)) + + peeked = pygame.event.peek(event_types) + + self.assertTrue(peeked) + + def test_peek__empty_queue(self): + """Ensure peek() works correctly on an empty queue.""" + pygame.event.clear() + + # Ensure all events can be checked. + peeked = pygame.event.peek() + + self.assertFalse(peeked) + + # Ensure events can be checked individually. + for event_type in EVENT_TYPES: + peeked = pygame.event.peek(event_type) + self.assertFalse(peeked) + + # Ensure events can be checked as a sequence. + peeked = pygame.event.peek(EVENT_TYPES) + + self.assertFalse(peeked) + + def test_set_allowed(self): + """Ensure a blocked event type can be unblocked/allowed.""" + event = EVENT_TYPES[0] + pygame.event.set_blocked(event) + + self.assertTrue(pygame.event.get_blocked(event)) + + pygame.event.set_allowed(event) + + self.assertFalse(pygame.event.get_blocked(event)) + + def test_set_allowed__event_sequence(self): + """Ensure a sequence of blocked event types can be unblocked/allowed. + """ + event_types = [ + pygame.KEYDOWN, + pygame.KEYUP, + pygame.MOUSEMOTION, + pygame.MOUSEBUTTONDOWN, + pygame.MOUSEBUTTONUP, + ] + pygame.event.set_blocked(event_types) + + pygame.event.set_allowed(event_types) + + for etype in event_types: + self.assertFalse(pygame.event.get_blocked(etype)) + + def test_set_allowed_all(self): + """Ensure all events can be unblocked/allowed at once.""" + pygame.event.set_blocked(None) + + for e in EVENT_TYPES: + self.assertTrue(pygame.event.get_blocked(e)) + + pygame.event.set_allowed(None) + + for e in EVENT_TYPES: + self.assertFalse(pygame.event.get_blocked(e)) + + def test_pump(self): + """Ensure pump() functions properly.""" + pygame.event.pump() + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", + 'requires the SDL_VIDEODRIVER to be a non "dummy" value', + ) + def test_set_grab__and_get_symmetric(self): + """Ensure event grabbing can be enabled and disabled. + + WARNING: Moving the mouse off the display during this test can cause it + to fail. + """ + surf = pygame.display.set_mode((10, 10)) + pygame.event.set_grab(True) + + self.assertTrue(pygame.event.get_grab()) + + pygame.event.set_grab(False) + + self.assertFalse(pygame.event.get_grab()) + + def test_event_equality(self): + a = pygame.event.Event(EVENT_TYPES[0], a=1) + b = pygame.event.Event(EVENT_TYPES[0], a=1) + c = pygame.event.Event(EVENT_TYPES[1], a=1) + d = pygame.event.Event(EVENT_TYPES[0], a=2) + + self.assertTrue(a == a) + self.assertFalse(a != a) + self.assertTrue(a == b) + self.assertFalse(a != b) + self.assertTrue(a != c) + self.assertFalse(a == c) + self.assertTrue(a != d) + self.assertFalse(a == d) + + def test_custom_type(self): + atype = pygame.event.custom_type() + atype2 = pygame.event.custom_type() + + self.assertEqual(atype, atype2 - 1) + + ev = pygame.event.Event(atype) + pygame.event.post(ev) + queue = pygame.event.get(atype) + self.assertEqual(len(queue), 1) + self.assertEqual(queue[0].type, atype) + + def test_get_blocked(self): + """Ensure an event's blocked state can be retrieved.""" + # Test each event is not blocked. + pygame.event.set_allowed(None) + + for etype in EVENT_TYPES: + blocked = pygame.event.get_blocked(etype) + + self.assertFalse(blocked) + + # Test each event type is blocked. + pygame.event.set_blocked(None) + + for etype in EVENT_TYPES: + blocked = pygame.event.get_blocked(etype) + + self.assertTrue(blocked) + + def test_get_blocked__event_sequence(self): + """Ensure get_blocked() can handle a sequence of event types.""" + event_types = [ + pygame.KEYDOWN, + pygame.KEYUP, + pygame.MOUSEMOTION, + pygame.MOUSEBUTTONDOWN, + pygame.MOUSEBUTTONUP, + ] + + # Test no event types in the list are blocked. + blocked = pygame.event.get_blocked(event_types) + + self.assertFalse(blocked) + + # Test when 1 event type in the list is blocked. + pygame.event.set_blocked(event_types[2]) + + blocked = pygame.event.get_blocked(event_types) + + self.assertTrue(blocked) + + # Test all event types in the list are blocked. + pygame.event.set_blocked(event_types) + + blocked = pygame.event.get_blocked(event_types) + + self.assertTrue(blocked) + + def todo_test_get_grab(self): + + # __doc__ (as of 2008-08-02) for pygame.event.get_grab: + + # pygame.event.get_grab(): return bool + # test if the program is sharing input devices + # + # Returns true when the input events are grabbed for this application. + # Use pygame.event.set_grab() to control this state. + # + + self.fail() + + def todo_test_poll(self): + + # __doc__ (as of 2008-08-02) for pygame.event.poll: + + # pygame.event.poll(): return Event + # get a single event from the queue + # + # Returns a single event from the queue. If the event queue is empty + # an event of type pygame.NOEVENT will be returned immediately. The + # returned event is removed from the queue. + # + + self.fail() + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/fastevent_tags.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fastevent_tags.py new file mode 100644 index 0000000..c660bef --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fastevent_tags.py @@ -0,0 +1 @@ +__tags__ = [] diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/fastevent_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fastevent_test.py new file mode 100644 index 0000000..f4a8949 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fastevent_test.py @@ -0,0 +1,158 @@ +import unittest +from pygame.tests.event_test import race_condition_notification +import pygame +from pygame import event, fastevent +from pygame.compat import geterror + +################################################################################ + + +class FasteventModuleTest(unittest.TestCase): + def setUp(self): + pygame.display.init() + fastevent.init() + event.clear() + + def tearDown(self): + # fastevent.quit() # Does not exist! + pygame.display.quit() + + def test_init(self): + # Test if module initialized after multiple init() calls. + fastevent.init() + fastevent.init() + + self.assertTrue(fastevent.get_init()) + + def test_auto_quit(self): + # Test if module uninitialized after calling pygame.quit(). + pygame.quit() + + self.assertFalse(fastevent.get_init()) + + def test_get_init(self): + # Test if get_init() gets the init state. + self.assertTrue(fastevent.get_init()) + + def test_get(self): + # __doc__ (as of 2008-08-02) for pygame.fastevent.get: + + # pygame.fastevent.get() -> list of Events + # get all events from the queue + + for _ in range(1, 11): + event.post(event.Event(pygame.USEREVENT)) + + self.assertListEqual( + [e.type for e in fastevent.get()], + [pygame.USEREVENT] * 10, + race_condition_notification, + ) + + def test_poll(self): + + # __doc__ (as of 2008-08-02) for pygame.fastevent.poll: + + # pygame.fastevent.poll() -> Event + # get an available event + # + # Returns next event on queue. If there is no event waiting on the + # queue, this will return an event with type NOEVENT. + + self.assertEqual( + fastevent.poll().type, pygame.NOEVENT, race_condition_notification + ) + + def test_post(self): + + # __doc__ (as of 2008-08-02) for pygame.fastevent.post: + + # pygame.fastevent.post(Event) -> None + # place an event on the queue + # + # This will post your own event objects onto the event queue. + # You can past any event type you want, but some care must be + # taken. For example, if you post a MOUSEBUTTONDOWN event to the + # queue, it is likely any code receiving the event will expect + # the standard MOUSEBUTTONDOWN attributes to be available, like + # 'pos' and 'button'. + # + # Because pygame.fastevent.post() may have to wait for the queue + # to empty, you can get into a dead lock if you try to append an + # event on to a full queue from the thread that processes events. + # For that reason I do not recommend using this function in the + # main thread of an SDL program. + + for _ in range(1, 11): + fastevent.post(event.Event(pygame.USEREVENT)) + + self.assertListEqual( + [e.type for e in event.get()], + [pygame.USEREVENT] * 10, + race_condition_notification, + ) + + try: + # Special case for post: METH_O. + fastevent.post(1) + except TypeError: + e = geterror() + msg = "argument 1 must be %s, not %s" % ( + fastevent.Event.__name__, + type(1).__name__, + ) + self.assertEqual(str(e), msg) + else: + self.fail() + + def test_post__clear(self): + """Ensure posted events can be cleared.""" + for _ in range(10): + fastevent.post(event.Event(pygame.USEREVENT)) + + event.clear() + + self.assertListEqual(fastevent.get(), []) + self.assertListEqual(event.get(), []) + + def todo_test_pump(self): + + # __doc__ (as of 2008-08-02) for pygame.fastevent.pump: + + # pygame.fastevent.pump() -> None + # update the internal messages + # + # For each frame of your game, you will need to make some sort + # of call to the event queue. This ensures your program can internally + # interact with the rest of the operating system. If you are not using + # other event functions in your game, you should call pump() to allow + # pygame to handle internal actions. + # + # There are important things that must be dealt with internally in the + # event queue. The main window may need to be repainted. Certain joysticks + # must be polled for their values. If you fail to make a call to the event + # queue for too long, the system may decide your program has locked up. + + self.fail() + + def test_wait(self): + + # __doc__ (as of 2008-08-02) for pygame.fastevent.wait: + + # pygame.fastevent.wait() -> Event + # wait for an event + # + # Returns the current event on the queue. If there are no messages + # waiting on the queue, this will not return until one is + # available. Sometimes it is important to use this wait to get + # events from the queue, it will allow your application to idle + # when the user isn't doing anything with it. + + event.post(pygame.event.Event(1)) + self.assertEqual(fastevent.wait().type, 1, race_condition_notification) + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/A_PyGameMono-8.png b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/A_PyGameMono-8.png new file mode 100644 index 0000000..b15961f Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/A_PyGameMono-8.png differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/PyGameMono-18-100dpi.bdf b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/PyGameMono-18-100dpi.bdf new file mode 100644 index 0000000..a88f083 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/PyGameMono-18-100dpi.bdf @@ -0,0 +1,165 @@ +STARTFONT 2.1 +FONT -FontForge-PyGameMono-Medium-R-Normal--25-180-100-100-M-250-ISO10646-1 +SIZE 18 100 100 +FONTBOUNDINGBOX 21 22 0 0 +COMMENT "Generated by fontforge, http://fontforge.sourceforge.net" +COMMENT "Created by Lenard Lindstrom,,, with FontForge 2.0 (http://fontforge.sf.net)" +STARTPROPERTIES 29 +FOUNDRY "FontForge" +FAMILY_NAME "PyGameMono" +WEIGHT_NAME "Medium" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "" +PIXEL_SIZE 25 +POINT_SIZE 180 +RESOLUTION_X 100 +RESOLUTION_Y 100 +SPACING "M" +AVERAGE_WIDTH 250 +CHARSET_REGISTRY "ISO10646" +CHARSET_ENCODING "1" +FONTNAME_REGISTRY "" +CHARSET_COLLECTIONS "ISO10646-1" +FONT_NAME "PyGameMono" +FACE_NAME "PyGame Mono" +FONT_VERSION "001.000" +FONT_ASCENT 20 +FONT_DESCENT 5 +UNDERLINE_POSITION -2 +UNDERLINE_THICKNESS 2 +RAW_ASCENT 800 +RAW_DESCENT 200 +RELATIVE_WEIGHT 50 +RELATIVE_SETWIDTH 50 +FIGURE_WIDTH -1 +AVG_UPPERCASE_WIDTH 250 +ENDPROPERTIES +CHARS 5 +STARTCHAR .notdef +ENCODING 0 +SWIDTH 1000 0 +DWIDTH 25 0 +BBX 20 20 0 0 +BITMAP +FFFFF0 +FFFFF0 +FE07F0 +F801F0 +F000F0 +E00070 +E00070 +C00030 +C00030 +C00030 +C00030 +C00030 +C00030 +E00070 +E00070 +F000F0 +F801F0 +FE07F0 +FFFFF0 +FFFFF0 +ENDCHAR +STARTCHAR A +ENCODING 65 +SWIDTH 1000 0 +DWIDTH 25 0 +BBX 20 21 0 1 +BITMAP +03FC00 +1FFF80 +3FFFC0 +7C03E0 +F000F0 +E00070 +E00070 +F000F0 +FC03F0 +FFFFF0 +FFFFF0 +FFFFF0 +FF0FF0 +7C03F0 +7801E0 +7800E0 +7000E0 +700060 +600060 +200040 +200040 +ENDCHAR +STARTCHAR B +ENCODING 66 +SWIDTH 1000 0 +DWIDTH 25 0 +BBX 18 20 1 0 +BITMAP +FFFE00 +FFFF80 +7E0780 +7801C0 +7000C0 +3000C0 +3000C0 +3801C0 +3E0780 +3FFF00 +3FFF00 +3E0780 +380180 +3000C0 +3000C0 +3000C0 +7801C0 +7E07C0 +FFFF80 +FFFE00 +ENDCHAR +STARTCHAR C +ENCODING 67 +SWIDTH 1000 0 +DWIDTH 25 0 +BBX 20 20 0 0 +BITMAP +00FC00 +03FF00 +0FFF80 +1F03E0 +3E0070 +7C0010 +780000 +F80000 +F00000 +F00000 +F00000 +F00000 +F80000 +780000 +7C0010 +3E0070 +1F01E0 +0FFFC0 +03FF80 +00FE00 +ENDCHAR +STARTCHAR u13079 +ENCODING 77945 +SWIDTH 1000 0 +DWIDTH 25 0 +BBX 21 10 0 5 +BITMAP +03FC00 +0FFF80 +1E73C0 +78F8F0 +F0F878 +70F870 +3870E0 +1E03C0 +0FFF80 +03FC00 +ENDCHAR +ENDFONT diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/PyGameMono-18-75dpi.bdf b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/PyGameMono-18-75dpi.bdf new file mode 100644 index 0000000..127f704 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/PyGameMono-18-75dpi.bdf @@ -0,0 +1,143 @@ +STARTFONT 2.1 +FONT -FontForge-PyGameMono-Medium-R-Normal--19-180-75-75-M-190-ISO10646-1 +SIZE 18 75 75 +FONTBOUNDINGBOX 15 17 0 0 +COMMENT "Generated by fontforge, http://fontforge.sourceforge.net" +COMMENT "Created by Lenard Lindstrom,,, with FontForge 2.0 (http://fontforge.sf.net)" +STARTPROPERTIES 29 +FOUNDRY "FontForge" +FAMILY_NAME "PyGameMono" +WEIGHT_NAME "Medium" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "" +PIXEL_SIZE 19 +POINT_SIZE 180 +RESOLUTION_X 75 +RESOLUTION_Y 75 +SPACING "M" +AVERAGE_WIDTH 190 +CHARSET_REGISTRY "ISO10646" +CHARSET_ENCODING "1" +FONTNAME_REGISTRY "" +CHARSET_COLLECTIONS "ISO10646-1" +FONT_NAME "PyGameMono" +FACE_NAME "PyGame Mono" +FONT_VERSION "001.000" +FONT_ASCENT 15 +FONT_DESCENT 4 +UNDERLINE_POSITION -2 +UNDERLINE_THICKNESS 1 +RAW_ASCENT 800 +RAW_DESCENT 200 +RELATIVE_WEIGHT 50 +RELATIVE_SETWIDTH 50 +FIGURE_WIDTH -1 +AVG_UPPERCASE_WIDTH 190 +ENDPROPERTIES +CHARS 5 +STARTCHAR .notdef +ENCODING 0 +SWIDTH 1000 0 +DWIDTH 19 0 +BBX 15 15 0 0 +BITMAP +FFFE +FFFE +FC7E +F01E +E00E +C006 +C006 +C006 +C006 +C006 +E00E +F01E +FC7E +FFFE +FFFE +ENDCHAR +STARTCHAR A +ENCODING 65 +SWIDTH 1000 0 +DWIDTH 19 0 +BBX 15 17 0 0 +BITMAP +0FE0 +3FF8 +783C +F01E +E00E +E00E +F01E +F83E +FFFE +FFFE +FC7E +701C +701C +600C +600C +4004 +4004 +ENDCHAR +STARTCHAR B +ENCODING 66 +SWIDTH 1000 0 +DWIDTH 19 0 +BBX 15 15 0 0 +BITMAP +FFF8 +7FFC +780E +3006 +3006 +380E +3FF8 +3FF8 +3FF8 +380E +3006 +3006 +7C1E +7FFC +FFF8 +ENDCHAR +STARTCHAR C +ENCODING 67 +SWIDTH 1000 0 +DWIDTH 19 0 +BBX 15 15 0 0 +BITMAP +03E0 +0FF8 +3C1C +7806 +7000 +E000 +E000 +E000 +E000 +E000 +7000 +7806 +3C1C +0FF8 +03E0 +ENDCHAR +STARTCHAR u13079 +ENCODING 77945 +SWIDTH 1000 0 +DWIDTH 19 0 +BBX 15 7 0 4 +BITMAP +0FE0 +3838 +638C +E38E +638C +3838 +0FE0 +ENDCHAR +ENDFONT diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/PyGameMono-8.bdf b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/PyGameMono-8.bdf new file mode 100644 index 0000000..17bef06 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/PyGameMono-8.bdf @@ -0,0 +1,103 @@ +STARTFONT 2.1 +FONT -FontForge-PyGameMono-Medium-R-Normal--8-80-75-75-C-80-ISO10646-1 +SIZE 8 75 75 +FONTBOUNDINGBOX 6 7 0 0 +COMMENT "Generated by fontforge, http://fontforge.sourceforge.net" +COMMENT "Created by Lenard Lindstrom,,, with FontForge 2.0 (http://fontforge.sf.net)" +STARTPROPERTIES 29 +FOUNDRY "FontForge" +FAMILY_NAME "PyGameMono" +WEIGHT_NAME "Medium" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "" +PIXEL_SIZE 8 +POINT_SIZE 80 +RESOLUTION_X 75 +RESOLUTION_Y 75 +SPACING "C" +AVERAGE_WIDTH 80 +CHARSET_REGISTRY "ISO10646" +CHARSET_ENCODING "1" +FONTNAME_REGISTRY "" +CHARSET_COLLECTIONS "ISO10646-1" +FONT_NAME "PyGameMono" +FACE_NAME "PyGame Mono" +FONT_VERSION "001.000" +FONT_ASCENT 6 +FONT_DESCENT 2 +UNDERLINE_POSITION -1 +UNDERLINE_THICKNESS 1 +RAW_ASCENT 800 +RAW_DESCENT 200 +RELATIVE_WEIGHT 50 +RELATIVE_SETWIDTH 50 +FIGURE_WIDTH -1 +AVG_UPPERCASE_WIDTH 80 +ENDPROPERTIES +CHARS 5 +STARTCHAR .notdef +ENCODING 0 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 6 6 0 0 +BITMAP +FC +84 +84 +84 +84 +FC +ENDCHAR +STARTCHAR A +ENCODING 65 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 6 7 0 0 +BITMAP +78 +84 +84 +FC +84 +84 +84 +ENDCHAR +STARTCHAR B +ENCODING 66 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 6 6 0 0 +BITMAP +FC +44 +78 +4C +44 +FC +ENDCHAR +STARTCHAR C +ENCODING 67 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 6 6 0 0 +BITMAP +78 +C4 +C0 +C0 +C4 +78 +ENDCHAR +STARTCHAR u13079 +ENCODING 77945 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 6 4 0 1 +BITMAP +78 +B4 +B4 +78 +ENDCHAR +ENDFONT diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/PyGameMono.otf b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/PyGameMono.otf new file mode 100644 index 0000000..5e9b66c Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/PyGameMono.otf differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/test_fixed.otf b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/test_fixed.otf new file mode 100644 index 0000000..3488898 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/test_fixed.otf differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/test_sans.ttf b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/test_sans.ttf new file mode 100644 index 0000000..09fac2f Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/test_sans.ttf differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/u13079_PyGameMono-8.png b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/u13079_PyGameMono-8.png new file mode 100644 index 0000000..911da8a Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/fonts/u13079_PyGameMono-8.png differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/xbm_cursors/white_sizing.xbm b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/xbm_cursors/white_sizing.xbm new file mode 100644 index 0000000..d334d8d --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/xbm_cursors/white_sizing.xbm @@ -0,0 +1,8 @@ +#define resize_white_width 16 +#define resize_white_height 16 +#define resize_white_x_hot 7 +#define resize_white_y_hot 7 +static unsigned char resize_white_bits[] = { + 0xff, 0x03, 0x01, 0x02, 0xfd, 0x03, 0x05, 0x00, 0xf5, 0x0f, 0x15, 0x08, + 0xd5, 0xeb, 0x55, 0xaa, 0x55, 0xaa, 0xd7, 0xab, 0x10, 0xa8, 0xf0, 0xb7, + 0x00, 0xa8, 0xc0, 0x9f, 0x40, 0x80, 0xc0, 0xff}; diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/xbm_cursors/white_sizing_mask.xbm b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/xbm_cursors/white_sizing_mask.xbm new file mode 100644 index 0000000..f00bc46 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/fixtures/xbm_cursors/white_sizing_mask.xbm @@ -0,0 +1,8 @@ +#define resize_white_mask_width 16 +#define resize_white_mask_height 16 +#define resize_white_mask_x_hot 7 +#define resize_white_mask_y_hot 7 +static unsigned char resize_white_mask_bits[] = { + 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0x07, 0x00, 0xf7, 0x0f, 0xf7, 0x0f, + 0xf7, 0xef, 0x77, 0xee, 0x77, 0xee, 0xf7, 0xef, 0xf0, 0xef, 0xf0, 0xff, + 0x00, 0xf8, 0xc0, 0xff, 0xc0, 0xff, 0xc0, 0xff}; diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/font_tags.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/font_tags.py new file mode 100644 index 0000000..c660bef --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/font_tags.py @@ -0,0 +1 @@ +__tags__ = [] diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/font_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/font_test.py new file mode 100644 index 0000000..48129a5 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/font_test.py @@ -0,0 +1,541 @@ +# -*- coding: utf8 -*- + +import sys +import os +import unittest +import platform + +import pygame +from pygame import font as pygame_font # So font can be replaced with ftfont +from pygame.compat import as_unicode, unicode_, as_bytes, xrange_, filesystem_errors +from pygame.compat import PY_MAJOR_VERSION + +FONTDIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures", "fonts") + +UCS_4 = sys.maxunicode > 0xFFFF + + +def equal_images(s1, s2): + size = s1.get_size() + if s2.get_size() != size: + return False + w, h = size + for x in xrange_(w): + for y in xrange_(h): + if s1.get_at((x, y)) != s2.get_at((x, y)): + return False + return True + + +IS_PYPY = "PyPy" == platform.python_implementation() + + +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO +class FontModuleTest(unittest.TestCase): + def setUp(self): + pygame_font.init() + + def tearDown(self): + pygame_font.quit() + + def test_SysFont(self): + # Can only check that a font object is returned. + fonts = pygame_font.get_fonts() + if "arial" in fonts: + # Try to use arial font if it is there, rather than a random font + # which can be different depending on installed fonts on the system. + font_name = "arial" + else: + font_name = sorted(fonts)[0] + o = pygame_font.SysFont(font_name, 20) + self.assertTrue(isinstance(o, pygame_font.FontType)) + o = pygame_font.SysFont(font_name, 20, italic=True) + self.assertTrue(isinstance(o, pygame_font.FontType)) + o = pygame_font.SysFont(font_name, 20, bold=True) + self.assertTrue(isinstance(o, pygame_font.FontType)) + o = pygame_font.SysFont("thisisnotafont", 20) + self.assertTrue(isinstance(o, pygame_font.FontType)) + + def test_get_default_font(self): + self.assertEqual(pygame_font.get_default_font(), "freesansbold.ttf") + + def test_get_fonts_returns_something(self): + fnts = pygame_font.get_fonts() + self.assertTrue(fnts) + + # to test if some files exist... + # def XXtest_has_file_osx_10_5_sdk(self): + # import os + # f = "/Developer/SDKs/MacOSX10.5.sdk/usr/X11/include/ft2build.h" + # self.assertEqual(os.path.exists(f), True) + + # def XXtest_has_file_osx_10_4_sdk(self): + # import os + # f = "/Developer/SDKs/MacOSX10.4u.sdk/usr/X11R6/include/ft2build.h" + # self.assertEqual(os.path.exists(f), True) + + def test_get_fonts(self): + fnts = pygame_font.get_fonts() + + self.assertTrue(fnts, msg=repr(fnts)) + + if PY_MAJOR_VERSION >= 3: + # For Python 3.x, names will always be unicode strings. + name_types = (str,) + else: + # For Python 2.x, names may be either unicode or ascii strings. + name_types = (str, unicode) + + for name in fnts: + # note, on ubuntu 2.6 they are all unicode strings. + + self.assertTrue(isinstance(name, name_types), name) + # Font names can be comprised of only numeric characters, so + # just checking name.islower() will not work as expected here. + self.assertFalse(any(c.isupper() for c in name)) + self.assertTrue(name.isalnum(), name) + + def test_get_init(self): + self.assertTrue(pygame_font.get_init()) + pygame_font.quit() + self.assertFalse(pygame_font.get_init()) + + def test_init(self): + pygame_font.init() + + def test_match_font_all_exist(self): + fonts = pygame_font.get_fonts() + + # Ensure all listed fonts are in fact available, and the returned file + # name is a full path. + for font in fonts: + path = pygame_font.match_font(font) + self.assertFalse(path is None) + self.assertTrue(os.path.isabs(path)) + + def test_match_font_bold(self): + fonts = pygame_font.get_fonts() + + # Look for a bold font. + self.assertTrue(any(pygame_font.match_font(font, bold=True) for font in fonts)) + + def test_match_font_italic(self): + fonts = pygame_font.get_fonts() + + # Look for an italic font. + self.assertTrue( + any(pygame_font.match_font(font, italic=True) for font in fonts) + ) + + def test_match_font_comma_separated(self): + fonts = pygame_font.get_fonts() + + # Check for not found. + self.assertTrue(pygame_font.match_font("thisisnotafont") is None) + + # Check comma separated list. + names = ",".join(["thisisnotafont", fonts[-1], "anothernonfont"]) + self.assertFalse(pygame_font.match_font(names) is None) + names = ",".join(["thisisnotafont1", "thisisnotafont2", "thisisnotafont3"]) + self.assertTrue(pygame_font.match_font(names) is None) + + def test_quit(self): + pygame_font.quit() + + +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO +class FontTest(unittest.TestCase): + def setUp(self): + pygame_font.init() + + def tearDown(self): + pygame_font.quit() + + def test_render_args(self): + screen = pygame.display.set_mode((600, 400)) + rect = screen.get_rect() + f = pygame_font.Font(None, 20) + screen.fill((10, 10, 10)) + font_surface = f.render(" bar", True, (0, 0, 0), (255, 255, 255)) + font_rect = font_surface.get_rect() + font_rect.topleft = rect.topleft + self.assertTrue(font_surface) + screen.blit(font_surface, font_rect, font_rect) + pygame.display.update() + self.assertEqual(tuple(screen.get_at((0, 0)))[:3], (255, 255, 255)) + self.assertEqual(tuple(screen.get_at(font_rect.topleft))[:3], (255, 255, 255)) + + # If we don't have a real display, don't do this test. + # Transparent background doesn't seem to work without a read video card. + if os.environ.get("SDL_VIDEODRIVER") != "dummy": + screen.fill((10, 10, 10)) + font_surface = f.render(" bar", True, (0, 0, 0), None) + font_rect = font_surface.get_rect() + font_rect.topleft = rect.topleft + self.assertTrue(font_surface) + screen.blit(font_surface, font_rect, font_rect) + pygame.display.update() + self.assertEqual(tuple(screen.get_at((0, 0)))[:3], (10, 10, 10)) + self.assertEqual(tuple(screen.get_at(font_rect.topleft))[:3], (10, 10, 10)) + + screen.fill((10, 10, 10)) + font_surface = f.render(" bar", True, (0, 0, 0)) + font_rect = font_surface.get_rect() + font_rect.topleft = rect.topleft + self.assertTrue(font_surface) + screen.blit(font_surface, font_rect, font_rect) + pygame.display.update(rect) + self.assertEqual(tuple(screen.get_at((0, 0)))[:3], (10, 10, 10)) + self.assertEqual(tuple(screen.get_at(font_rect.topleft))[:3], (10, 10, 10)) + + +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO +class FontTypeTest(unittest.TestCase): + def setUp(self): + pygame_font.init() + + def tearDown(self): + pygame_font.quit() + + def test_get_ascent(self): + # Ckecking ascent would need a custom test font to do properly. + f = pygame_font.Font(None, 20) + ascent = f.get_ascent() + self.assertTrue(isinstance(ascent, int)) + self.assertTrue(ascent > 0) + s = f.render("X", False, (255, 255, 255)) + self.assertTrue(s.get_size()[1] > ascent) + + def test_get_descent(self): + # Ckecking descent would need a custom test font to do properly. + f = pygame_font.Font(None, 20) + descent = f.get_descent() + self.assertTrue(isinstance(descent, int)) + self.assertTrue(descent < 0) + + def test_get_height(self): + # Ckecking height would need a custom test font to do properly. + f = pygame_font.Font(None, 20) + height = f.get_height() + self.assertTrue(isinstance(height, int)) + self.assertTrue(height > 0) + s = f.render("X", False, (255, 255, 255)) + self.assertTrue(s.get_size()[1] == height) + + def test_get_linesize(self): + # Ckecking linesize would need a custom test font to do properly. + # Questions: How do linesize, height and descent relate? + f = pygame_font.Font(None, 20) + linesize = f.get_linesize() + self.assertTrue(isinstance(linesize, int)) + self.assertTrue(linesize > 0) + + def test_metrics(self): + # Ensure bytes decoding works correctly. Can only compare results + # with unicode for now. + f = pygame_font.Font(None, 20) + um = f.metrics(as_unicode(".")) + bm = f.metrics(as_bytes(".")) + + self.assertEqual(len(um), 1) + self.assertEqual(len(bm), 1) + self.assertIsNotNone(um[0]) + self.assertEqual(um, bm) + + u = u"\u212A" + b = u.encode("UTF-16")[2:] # Keep byte order consistent. [2:] skips BOM + bm = f.metrics(b) + + self.assertEqual(len(bm), 2) + + try: # FIXME why do we do this try/except ? + um = f.metrics(u) + except pygame.error: + pass + else: + self.assertEqual(len(um), 1) + self.assertNotEqual(bm[0], um[0]) + self.assertNotEqual(bm[1], um[0]) + + if UCS_4: + u = u"\U00013000" + bm = f.metrics(u) + + self.assertEqual(len(bm), 1) + self.assertIsNone(bm[0]) + + return # unfinished + # The documentation is useless here. How large a list? + # How do list positions relate to character codes? + # What about unicode characters? + + # __doc__ (as of 2008-08-02) for pygame_font.Font.metrics: + + # Font.metrics(text): return list + # Gets the metrics for each character in the pased string. + # + # The list contains tuples for each character, which contain the + # minimum X offset, the maximum X offset, the minimum Y offset, the + # maximum Y offset and the advance offset (bearing plus width) of the + # character. [(minx, maxx, miny, maxy, advance), (minx, maxx, miny, + # maxy, advance), ...] + + self.fail() + + def test_render(self): + f = pygame_font.Font(None, 20) + s = f.render("foo", True, [0, 0, 0], [255, 255, 255]) + s = f.render("xxx", True, [0, 0, 0], [255, 255, 255]) + s = f.render("", True, [0, 0, 0], [255, 255, 255]) + s = f.render("foo", False, [0, 0, 0], [255, 255, 255]) + s = f.render("xxx", False, [0, 0, 0], [255, 255, 255]) + s = f.render("xxx", False, [0, 0, 0]) + s = f.render(" ", False, [0, 0, 0]) + s = f.render(" ", False, [0, 0, 0], [255, 255, 255]) + # null text should be 0 pixel wide. + s = f.render("", False, [0, 0, 0], [255, 255, 255]) + self.assertEqual(s.get_size()[0], 0) + # None text should be 0 pixel wide. + s = f.render(None, False, [0, 0, 0], [255, 255, 255]) + self.assertEqual(s.get_size()[0], 0) + # Non-text should raise a TypeError. + self.assertRaises(TypeError, f.render, [], False, [0, 0, 0], [255, 255, 255]) + self.assertRaises(TypeError, f.render, 1, False, [0, 0, 0], [255, 255, 255]) + # is background transparent for antialiasing? + s = f.render(".", True, [255, 255, 255]) + self.assertEqual(s.get_at((0, 0))[3], 0) + # is Unicode and bytes encoding correct? + # Cannot really test if the correct characters are rendered, but + # at least can assert the encodings differ. + su = f.render(as_unicode("."), False, [0, 0, 0], [255, 255, 255]) + sb = f.render(as_bytes("."), False, [0, 0, 0], [255, 255, 255]) + self.assertTrue(equal_images(su, sb)) + u = as_unicode(r"\u212A") + b = u.encode("UTF-16")[2:] # Keep byte order consistent. [2:] skips BOM + sb = f.render(b, False, [0, 0, 0], [255, 255, 255]) + try: # FIXME why do we do this try/except ? + su = f.render(u, False, [0, 0, 0], [255, 255, 255]) + except pygame.error: + pass + else: + self.assertFalse(equal_images(su, sb)) + + # If the font module is SDL_ttf based, then it can only supports UCS-2; + # it will raise an exception for an out-of-range UCS-4 code point. + if UCS_4 and not hasattr(f, "ucs4"): + ucs_2 = as_unicode(r"\uFFEE") + s = f.render(ucs_2, False, [0, 0, 0], [255, 255, 255]) + ucs_4 = as_unicode(r"\U00010000") + self.assertRaises( + UnicodeError, f.render, ucs_4, False, [0, 0, 0], [255, 255, 255] + ) + + b = as_bytes("ab\x00cd") + self.assertRaises(ValueError, f.render, b, 0, [0, 0, 0]) + u = as_unicode("ab\x00cd") + self.assertRaises(ValueError, f.render, b, 0, [0, 0, 0]) + + def test_set_bold(self): + f = pygame_font.Font(None, 20) + self.assertFalse(f.get_bold()) + f.set_bold(True) + self.assertTrue(f.get_bold()) + f.set_bold(False) + self.assertFalse(f.get_bold()) + + def test_set_italic(self): + f = pygame_font.Font(None, 20) + self.assertFalse(f.get_italic()) + f.set_italic(True) + self.assertTrue(f.get_italic()) + f.set_italic(False) + self.assertFalse(f.get_italic()) + + def test_set_underline(self): + f = pygame_font.Font(None, 20) + self.assertFalse(f.get_underline()) + f.set_underline(True) + self.assertTrue(f.get_underline()) + f.set_underline(False) + self.assertFalse(f.get_underline()) + + def test_size(self): + f = pygame_font.Font(None, 20) + text = as_unicode("Xg") + size = f.size(text) + w, h = size + s = f.render(text, False, (255, 255, 255)) + btext = text.encode("ascii") + + self.assertIsInstance(w, int) + self.assertIsInstance(h, int) + self.assertEqual(s.get_size(), size) + self.assertEqual(f.size(btext), size) + + text = as_unicode(r"\u212A") + btext = text.encode("UTF-16")[2:] # Keep the byte order consistent. + bsize = f.size(btext) + try: # FIXME why do we do this try/except ? + size = f.size(text) + except pygame.error: + pass + else: + self.assertNotEqual(size, bsize) + + def test_font_file_not_found(self): + # A per BUG reported by Bo Jangeborg on pygame-user mailing list, + # http://www.mail-archive.com/pygame-users@seul.org/msg11675.html + + pygame_font.init() + self.assertRaises( + IOError, pygame_font.Font, unicode_("some-fictional-font.ttf"), 20 + ) + + def test_load_from_file(self): + font_name = pygame_font.get_default_font() + font_path = os.path.join( + os.path.split(pygame.__file__)[0], pygame_font.get_default_font() + ) + f = pygame_font.Font(font_path, 20) + + def test_load_from_file_obj(self): + font_name = pygame_font.get_default_font() + font_path = os.path.join( + os.path.split(pygame.__file__)[0], pygame_font.get_default_font() + ) + with open(font_path, "rb") as f: + font = pygame_font.Font(f, 20) + + def test_load_default_font_filename(self): + # In font_init, a special case is when the filename argument is + # identical to the default font file name. + f = pygame_font.Font(pygame_font.get_default_font(), 20) + + def _load_unicode(self, path): + import shutil + + fdir = unicode_(FONTDIR) + temp = os.path.join(fdir, path) + pgfont = os.path.join(fdir, u"test_sans.ttf") + shutil.copy(pgfont, temp) + try: + with open(temp, "rb") as f: + pass + except IOError: + raise unittest.SkipTest("the path cannot be opened") + try: + pygame_font.Font(temp, 20) + finally: + os.remove(temp) + + def test_load_from_file_unicode_0(self): + """ASCII string as a unicode object""" + self._load_unicode(u"temp_file.ttf") + + def test_load_from_file_unicode_1(self): + self._load_unicode(u"你好.ttf") + + def test_load_from_file_bytes(self): + font_path = os.path.join( + os.path.split(pygame.__file__)[0], pygame_font.get_default_font() + ) + filesystem_encoding = sys.getfilesystemencoding() + try: # FIXME why do we do this try/except ? + font_path = font_path.decode(filesystem_encoding, filesystem_errors) + except AttributeError: + pass + bfont_path = font_path.encode(filesystem_encoding, filesystem_errors) + f = pygame_font.Font(bfont_path, 20) + + +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO +class VisualTests(unittest.TestCase): + + __tags__ = ["interactive"] + + screen = None + aborted = False + + def setUp(self): + if self.screen is None: + pygame.init() + self.screen = pygame.display.set_mode((600, 200)) + self.screen.fill((255, 255, 255)) + pygame.display.flip() + self.f = pygame_font.Font(None, 32) + + def abort(self): + if self.screen is not None: + pygame.quit() + self.aborted = True + + def query(self, bold=False, italic=False, underline=False, antialiase=False): + if self.aborted: + return False + spacing = 10 + offset = 20 + y = spacing + f = self.f + screen = self.screen + screen.fill((255, 255, 255)) + pygame.display.flip() + if not (bold or italic or underline or antialiase): + text = "normal" + else: + modes = [] + if bold: + modes.append("bold") + if italic: + modes.append("italic") + if underline: + modes.append("underlined") + if antialiase: + modes.append("antialiased") + text = "%s (y/n):" % ("-".join(modes),) + f.set_bold(bold) + f.set_italic(italic) + f.set_underline(underline) + s = f.render(text, antialiase, (0, 0, 0)) + screen.blit(s, (offset, y)) + y += s.get_size()[1] + spacing + f.set_bold(False) + f.set_italic(False) + f.set_underline(False) + s = f.render("(some comparison text)", False, (0, 0, 0)) + screen.blit(s, (offset, y)) + pygame.display.flip() + while 1: + for evt in pygame.event.get(): + if evt.type == pygame.KEYDOWN: + if evt.key == pygame.K_ESCAPE: + self.abort() + return False + if evt.key == pygame.K_y: + return True + if evt.key == pygame.K_n: + return False + if evt.type == pygame.QUIT: + self.abort() + return False + + def test_bold(self): + self.assertTrue(self.query(bold=True)) + + def test_italic(self): + self.assertTrue(self.query(italic=True)) + + def test_underline(self): + self.assertTrue(self.query(underline=True)) + + def test_antialiase(self): + self.assertTrue(self.query(antialiase=True)) + + def test_bold_antialiase(self): + self.assertTrue(self.query(bold=True, antialiase=True)) + + def test_italic_underline(self): + self.assertTrue(self.query(italic=True, underline=True)) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/freetype_tags.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/freetype_tags.py new file mode 100644 index 0000000..d84cbb7 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/freetype_tags.py @@ -0,0 +1,11 @@ +__tags__ = ["development"] + +exclude = False + +try: + import pygame.freetype +except ImportError: + exclude = True + +if exclude: + __tags__.extend(["ignore", "subprocess_ignore"]) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/freetype_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/freetype_test.py new file mode 100644 index 0000000..22d9633 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/freetype_test.py @@ -0,0 +1,1739 @@ +import os + +if os.environ.get("SDL_VIDEODRIVER") == "dummy": + __tags__ = ("ignore", "subprocess_ignore") + +import unittest +import sys +import ctypes +import weakref +import gc +import platform + +IS_PYPY = "PyPy" == platform.python_implementation() + + +try: + from pygame.tests.test_utils import arrinter +except NameError: + pass + +import pygame + +try: + import pygame.freetype as ft +except ImportError: + ft = None +from pygame.compat import as_unicode, bytes_, unichr_, unicode_ + + +FONTDIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures", "fonts") + + +def nullfont(): + """return an uninitialized font instance""" + return ft.Font.__new__(ft.Font) + + +max_point_size_FX6 = 0x7FFFFFFF +max_point_size = max_point_size_FX6 >> 6 +max_point_size_f = max_point_size_FX6 * 0.015625 + + +def surf_same_image(a, b): + """Return True if a's pixel buffer is identical to b's""" + + a_sz = a.get_height() * a.get_pitch() + b_sz = b.get_height() * b.get_pitch() + if a_sz != b_sz: + return False + a_bytes = ctypes.string_at(a._pixels_address, a_sz) + b_bytes = ctypes.string_at(b._pixels_address, b_sz) + return a_bytes == b_bytes + + +class FreeTypeFontTest(unittest.TestCase): + + _fixed_path = os.path.join(FONTDIR, "test_fixed.otf") + _sans_path = os.path.join(FONTDIR, "test_sans.ttf") + _mono_path = os.path.join(FONTDIR, "PyGameMono.otf") + _bmp_8_75dpi_path = os.path.join(FONTDIR, "PyGameMono-8.bdf") + _bmp_18_75dpi_path = os.path.join(FONTDIR, "PyGameMono-18-75dpi.bdf") + _bmp_18_100dpi_path = os.path.join(FONTDIR, "PyGameMono-18-100dpi.bdf") + _TEST_FONTS = {} + + @classmethod + def setUpClass(cls): + ft.init() + + # Setup the test fonts. + + # Inconsolata is an open-source font designed by Raph Levien. + # Licensed under the Open Font License. + # http://www.levien.com/type/myfonts/inconsolata.html + cls._TEST_FONTS["fixed"] = ft.Font(cls._fixed_path) + + # Liberation Sans is an open-source font designed by Steve Matteson. + # Licensed under the GNU GPL. + # https://fedorahosted.org/liberation-fonts/ + cls._TEST_FONTS["sans"] = ft.Font(cls._sans_path) + + # A scalable mono test font made for pygame. It contains only + # a few glyphs: '\0', 'A', 'B', 'C', and U+13079. + # It also contains two bitmap sizes: 8.0 X 8.0 and 19.0 X 19.0. + cls._TEST_FONTS["mono"] = ft.Font(cls._mono_path) + + # A fixed size bitmap mono test font made for pygame. + # It contains only a few glyphs: '\0', 'A', 'B', 'C', and U+13079. + # The size is 8.0 X 8.0. + cls._TEST_FONTS["bmp-8-75dpi"] = ft.Font(cls._bmp_8_75dpi_path) + + # A fixed size bitmap mono test font made for pygame. + # It contains only a few glyphs: '\0', 'A', 'B', 'C', and U+13079. + # The size is 8.0 X 8.0. + cls._TEST_FONTS["bmp-18-75dpi"] = ft.Font(cls._bmp_18_75dpi_path) + + # A fixed size bitmap mono test font made for pygame. + # It contains only a few glyphs: '\0', 'A', 'B', 'C', and U+13079. + # The size is 8.0 X 8.0. + cls._TEST_FONTS["bmp-18-100dpi"] = ft.Font(cls._bmp_18_100dpi_path) + + @classmethod + def tearDownClass(cls): + ft.quit() + + def test_freetype_defaultfont(self): + font = ft.Font(None) + self.assertEqual(font.name, "FreeSans") + + def test_freetype_Font_init(self): + + self.assertRaises(IOError, ft.Font, os.path.join(FONTDIR, "nonexistant.ttf")) + + f = self._TEST_FONTS["sans"] + self.assertIsInstance(f, ft.Font) + + f = self._TEST_FONTS["fixed"] + self.assertIsInstance(f, ft.Font) + + # Test keyword arguments + f = ft.Font(size=22, file=None) + self.assertEqual(f.size, 22) + f = ft.Font(font_index=0, file=None) + self.assertNotEqual(ft.get_default_resolution(), 100) + f = ft.Font(resolution=100, file=None) + self.assertEqual(f.resolution, 100) + f = ft.Font(ucs4=True, file=None) + self.assertTrue(f.ucs4) + self.assertRaises(OverflowError, ft.Font, file=None, size=(max_point_size + 1)) + self.assertRaises(OverflowError, ft.Font, file=None, size=-1) + + f = ft.Font(None, size=24) + self.assertTrue(f.height > 0) + self.assertRaises(IOError, f.__init__, os.path.join(FONTDIR, "nonexistant.ttf")) + + # Test attribute preservation during reinitalization + f = ft.Font(self._sans_path, size=24, ucs4=True) + self.assertEqual(f.name, "Liberation Sans") + self.assertTrue(f.scalable) + self.assertFalse(f.fixed_width) + self.assertTrue(f.antialiased) + self.assertFalse(f.oblique) + self.assertTrue(f.ucs4) + f.antialiased = False + f.oblique = True + f.__init__(self._mono_path) + self.assertEqual(f.name, "PyGameMono") + self.assertTrue(f.scalable) + self.assertTrue(f.fixed_width) + self.assertFalse(f.antialiased) + self.assertTrue(f.oblique) + self.assertTrue(f.ucs4) + + # For a bitmap font, the size is automatically set to the first + # size in the available sizes list. + f = ft.Font(self._bmp_8_75dpi_path) + sizes = f.get_sizes() + self.assertEqual(len(sizes), 1) + size_pt, width_px, height_px, x_ppem, y_ppem = sizes[0] + self.assertEqual(f.size, (x_ppem, y_ppem)) + f.__init__(self._bmp_8_75dpi_path, size=12) + self.assertEqual(f.size, 12.0) + + @unittest.skipIf(IS_PYPY, "PyPy doesn't use refcounting") + def test_freetype_Font_dealloc(self): + import sys + + handle = open(self._sans_path, "rb") + + def load_font(): + tempFont = ft.Font(handle) + + try: + load_font() + + self.assertEqual(sys.getrefcount(handle), 2) + finally: + # Ensures file is closed even if test fails. + handle.close() + + def test_freetype_Font_kerning(self): + """Ensures get/set works with the kerning property.""" + ft_font = self._TEST_FONTS["sans"] + + # Test default is disabled. + self.assertFalse(ft_font.kerning) + + # Test setting to True. + ft_font.kerning = True + + self.assertTrue(ft_font.kerning) + + # Test setting to False. + ft_font.kerning = False + + self.assertFalse(ft_font.kerning) + + def test_freetype_Font_kerning__enabled(self): + """Ensures exceptions are not raised when calling freetype methods + while kerning is enabled. + + Note: This does not test what changes occur to a rendered font by + having kerning enabled. + + Related to issue #367. + """ + surface = pygame.Surface((10, 10), 0, 32) + TEST_TEXT = "Freetype Font" + ft_font = self._TEST_FONTS["bmp-8-75dpi"] + + ft_font.kerning = True + + # Call different methods to ensure they don't raise an exception. + metrics = ft_font.get_metrics(TEST_TEXT) + self.assertIsInstance(metrics, list) + + rect = ft_font.get_rect(TEST_TEXT) + self.assertIsInstance(rect, pygame.Rect) + + font_surf, rect = ft_font.render(TEST_TEXT) + self.assertIsInstance(font_surf, pygame.Surface) + self.assertIsInstance(rect, pygame.Rect) + + rect = ft_font.render_to(surface, (0, 0), TEST_TEXT) + self.assertIsInstance(rect, pygame.Rect) + + buf, size = ft_font.render_raw(TEST_TEXT) + self.assertIsInstance(buf, bytes_) + self.assertIsInstance(size, tuple) + + rect = ft_font.render_raw_to(surface.get_view("2"), TEST_TEXT) + self.assertIsInstance(rect, pygame.Rect) + + def test_freetype_Font_scalable(self): + + f = self._TEST_FONTS["sans"] + self.assertTrue(f.scalable) + + self.assertRaises(RuntimeError, lambda: nullfont().scalable) + + def test_freetype_Font_fixed_width(self): + + f = self._TEST_FONTS["sans"] + self.assertFalse(f.fixed_width) + + f = self._TEST_FONTS["mono"] + self.assertTrue(f.fixed_width) + + self.assertRaises(RuntimeError, lambda: nullfont().fixed_width) + + def test_freetype_Font_fixed_sizes(self): + + f = self._TEST_FONTS["sans"] + self.assertEqual(f.fixed_sizes, 0) + f = self._TEST_FONTS["bmp-8-75dpi"] + self.assertEqual(f.fixed_sizes, 1) + f = self._TEST_FONTS["mono"] + self.assertEqual(f.fixed_sizes, 2) + + def test_freetype_Font_get_sizes(self): + f = self._TEST_FONTS["sans"] + szlist = f.get_sizes() + self.assertIsInstance(szlist, list) + self.assertEqual(len(szlist), 0) + + f = self._TEST_FONTS["bmp-8-75dpi"] + szlist = f.get_sizes() + self.assertIsInstance(szlist, list) + self.assertEqual(len(szlist), 1) + + size8 = szlist[0] + self.assertIsInstance(size8[0], int) + self.assertEqual(size8[0], 8) + self.assertIsInstance(size8[1], int) + self.assertIsInstance(size8[2], int) + self.assertIsInstance(size8[3], float) + self.assertEqual(int(size8[3] * 64.0 + 0.5), 8 * 64) + self.assertIsInstance(size8[4], float) + self.assertEqual(int(size8[4] * 64.0 + 0.5), 8 * 64) + + f = self._TEST_FONTS["mono"] + szlist = f.get_sizes() + self.assertIsInstance(szlist, list) + self.assertEqual(len(szlist), 2) + + size8 = szlist[0] + self.assertEqual(size8[3], 8) + self.assertEqual(int(size8[3] * 64.0 + 0.5), 8 * 64) + self.assertEqual(int(size8[4] * 64.0 + 0.5), 8 * 64) + + size19 = szlist[1] + self.assertEqual(size19[3], 19) + self.assertEqual(int(size19[3] * 64.0 + 0.5), 19 * 64) + self.assertEqual(int(size19[4] * 64.0 + 0.5), 19 * 64) + + def test_freetype_Font_use_bitmap_strikes(self): + f = self._TEST_FONTS["mono"] + try: + # use_bitmap_strikes == True + # + self.assertTrue(f.use_bitmap_strikes) + + # bitmap compatible properties + s_strike, sz = f.render_raw("A", size=19) + try: + f.vertical = True + s_strike_vert, sz = f.render_raw("A", size=19) + finally: + f.vertical = False + try: + f.wide = True + s_strike_wide, sz = f.render_raw("A", size=19) + finally: + f.wide = False + try: + f.underline = True + s_strike_underline, sz = f.render_raw("A", size=19) + finally: + f.underline = False + + # bitmap incompatible properties + s_strike_rot45, sz = f.render_raw("A", size=19, rotation=45) + try: + f.strong = True + s_strike_strong, sz = f.render_raw("A", size=19) + finally: + f.strong = False + try: + f.oblique = True + s_strike_oblique, sz = f.render_raw("A", size=19) + finally: + f.oblique = False + + # compare with use_bitmap_strikes == False + # + f.use_bitmap_strikes = False + self.assertFalse(f.use_bitmap_strikes) + + # bitmap compatible properties + s_outline, sz = f.render_raw("A", size=19) + self.assertNotEqual(s_outline, s_strike) + try: + f.vertical = True + s_outline, sz = f.render_raw("A", size=19) + self.assertNotEqual(s_outline, s_strike_vert) + finally: + f.vertical = False + try: + f.wide = True + s_outline, sz = f.render_raw("A", size=19) + self.assertNotEqual(s_outline, s_strike_wide) + finally: + f.wide = False + try: + f.underline = True + s_outline, sz = f.render_raw("A", size=19) + self.assertNotEqual(s_outline, s_strike_underline) + finally: + f.underline = False + + # bitmap incompatible properties + s_outline, sz = f.render_raw("A", size=19, rotation=45) + self.assertEqual(s_outline, s_strike_rot45) + try: + f.strong = True + s_outline, sz = f.render_raw("A", size=19) + self.assertEqual(s_outline, s_strike_strong) + finally: + f.strong = False + try: + f.oblique = True + s_outline, sz = f.render_raw("A", size=19) + self.assertEqual(s_outline, s_strike_oblique) + finally: + f.oblique = False + finally: + f.use_bitmap_strikes = True + + def test_freetype_Font_bitmap_files(self): + """Ensure bitmap file restrictions are caught""" + f = self._TEST_FONTS["bmp-8-75dpi"] + f_null = nullfont() + s = pygame.Surface((10, 10), 0, 32) + a = s.get_view("3") + + exception = AttributeError + self.assertRaises(exception, setattr, f, "strong", True) + self.assertRaises(exception, setattr, f, "oblique", True) + self.assertRaises(exception, setattr, f, "style", ft.STYLE_STRONG) + self.assertRaises(exception, setattr, f, "style", ft.STYLE_OBLIQUE) + exception = RuntimeError + self.assertRaises(exception, setattr, f_null, "strong", True) + self.assertRaises(exception, setattr, f_null, "oblique", True) + self.assertRaises(exception, setattr, f_null, "style", ft.STYLE_STRONG) + self.assertRaises(exception, setattr, f_null, "style", ft.STYLE_OBLIQUE) + exception = ValueError + self.assertRaises(exception, f.render, "A", (0, 0, 0), size=8, rotation=1) + self.assertRaises( + exception, f.render, "A", (0, 0, 0), size=8, style=ft.STYLE_OBLIQUE + ) + self.assertRaises( + exception, f.render, "A", (0, 0, 0), size=8, style=ft.STYLE_STRONG + ) + self.assertRaises(exception, f.render_raw, "A", size=8, rotation=1) + self.assertRaises(exception, f.render_raw, "A", size=8, style=ft.STYLE_OBLIQUE) + self.assertRaises(exception, f.render_raw, "A", size=8, style=ft.STYLE_STRONG) + self.assertRaises( + exception, f.render_to, s, (0, 0), "A", (0, 0, 0), size=8, rotation=1 + ) + self.assertRaises( + exception, + f.render_to, + s, + (0, 0), + "A", + (0, 0, 0), + size=8, + style=ft.STYLE_OBLIQUE, + ) + self.assertRaises( + exception, + f.render_to, + s, + (0, 0), + "A", + (0, 0, 0), + size=8, + style=ft.STYLE_STRONG, + ) + self.assertRaises(exception, f.render_raw_to, a, "A", size=8, rotation=1) + self.assertRaises( + exception, f.render_raw_to, a, "A", size=8, style=ft.STYLE_OBLIQUE + ) + self.assertRaises( + exception, f.render_raw_to, a, "A", size=8, style=ft.STYLE_STRONG + ) + self.assertRaises(exception, f.get_rect, "A", size=8, rotation=1) + self.assertRaises(exception, f.get_rect, "A", size=8, style=ft.STYLE_OBLIQUE) + self.assertRaises(exception, f.get_rect, "A", size=8, style=ft.STYLE_STRONG) + + # Unsupported point size + exception = pygame.error + self.assertRaises(exception, f.get_rect, "A", size=42) + self.assertRaises(exception, f.get_metrics, "A", size=42) + self.assertRaises(exception, f.get_sized_ascender, 42) + self.assertRaises(exception, f.get_sized_descender, 42) + self.assertRaises(exception, f.get_sized_height, 42) + self.assertRaises(exception, f.get_sized_glyph_height, 42) + + def test_freetype_Font_get_metrics(self): + + font = self._TEST_FONTS["sans"] + + metrics = font.get_metrics("ABCD", size=24) + self.assertEqual(len(metrics), len("ABCD")) + self.assertIsInstance(metrics, list) + + for metrics_tuple in metrics: + self.assertIsInstance(metrics_tuple, tuple, metrics_tuple) + self.assertEqual(len(metrics_tuple), 6) + + for m in metrics_tuple[:4]: + self.assertIsInstance(m, int) + + for m in metrics_tuple[4:]: + self.assertIsInstance(m, float) + + # test for empty string + metrics = font.get_metrics("", size=24) + self.assertEqual(metrics, []) + + # test for invalid string + self.assertRaises(TypeError, font.get_metrics, 24, 24) + + # raises exception when uninitalized + self.assertRaises(RuntimeError, nullfont().get_metrics, "a", size=24) + + def test_freetype_Font_get_rect(self): + + font = self._TEST_FONTS["sans"] + + def test_rect(r): + self.assertIsInstance(r, pygame.Rect) + + rect_default = font.get_rect("ABCDabcd", size=24) + test_rect(rect_default) + self.assertTrue(rect_default.size > (0, 0)) + self.assertTrue(rect_default.width > rect_default.height) + + rect_bigger = font.get_rect("ABCDabcd", size=32) + test_rect(rect_bigger) + self.assertTrue(rect_bigger.size > rect_default.size) + + rect_strong = font.get_rect("ABCDabcd", size=24, style=ft.STYLE_STRONG) + test_rect(rect_strong) + self.assertTrue(rect_strong.size > rect_default.size) + + font.vertical = True + rect_vert = font.get_rect("ABCDabcd", size=24) + test_rect(rect_vert) + self.assertTrue(rect_vert.width < rect_vert.height) + font.vertical = False + + rect_oblique = font.get_rect("ABCDabcd", size=24, style=ft.STYLE_OBLIQUE) + test_rect(rect_oblique) + self.assertTrue(rect_oblique.width > rect_default.width) + self.assertTrue(rect_oblique.height == rect_default.height) + + rect_under = font.get_rect("ABCDabcd", size=24, style=ft.STYLE_UNDERLINE) + test_rect(rect_under) + self.assertTrue(rect_under.width == rect_default.width) + self.assertTrue(rect_under.height > rect_default.height) + + # Rect size should change if UTF surrogate pairs are treated as + # one code point or two. + ufont = self._TEST_FONTS["mono"] + rect_utf32 = ufont.get_rect(as_unicode(r"\U00013079"), size=24) + rect_utf16 = ufont.get_rect(as_unicode(r"\uD80C\uDC79"), size=24) + self.assertEqual(rect_utf16, rect_utf32) + ufont.ucs4 = True + try: + rect_utf16 = ufont.get_rect(as_unicode(r"\uD80C\uDC79"), size=24) + finally: + ufont.ucs4 = False + self.assertNotEqual(rect_utf16, rect_utf32) + + self.assertRaises(RuntimeError, nullfont().get_rect, "a", size=24) + + # text stretching + rect12 = font.get_rect("A", size=12.0) + rect24 = font.get_rect("A", size=24.0) + rect_x = font.get_rect("A", size=(24.0, 12.0)) + self.assertEqual(rect_x.width, rect24.width) + self.assertEqual(rect_x.height, rect12.height) + rect_y = font.get_rect("A", size=(12.0, 24.0)) + self.assertEqual(rect_y.width, rect12.width) + self.assertEqual(rect_y.height, rect24.height) + + def test_freetype_Font_height(self): + + f = self._TEST_FONTS["sans"] + self.assertEqual(f.height, 2355) + + f = self._TEST_FONTS["fixed"] + self.assertEqual(f.height, 1100) + + self.assertRaises(RuntimeError, lambda: nullfont().height) + + def test_freetype_Font_name(self): + + f = self._TEST_FONTS["sans"] + self.assertEqual(f.name, "Liberation Sans") + + f = self._TEST_FONTS["fixed"] + self.assertEqual(f.name, "Inconsolata") + + nf = nullfont() + self.assertEqual(nf.name, repr(nf)) + + def test_freetype_Font_size(self): + + f = ft.Font(None, size=12) + self.assertEqual(f.size, 12) + f.size = 22 + self.assertEqual(f.size, 22) + f.size = 0 + self.assertEqual(f.size, 0) + f.size = max_point_size + self.assertEqual(f.size, max_point_size) + f.size = 6.5 + self.assertEqual(f.size, 6.5) + f.size = max_point_size_f + self.assertEqual(f.size, max_point_size_f) + self.assertRaises(OverflowError, setattr, f, "size", -1) + self.assertRaises(OverflowError, setattr, f, "size", (max_point_size + 1)) + + f.size = 24.0, 0 + size = f.size + self.assertIsInstance(size, float) + self.assertEqual(size, 24.0) + + f.size = 16, 16 + size = f.size + self.assertIsInstance(size, tuple) + self.assertEqual(len(size), 2) + + x, y = size + self.assertIsInstance(x, float) + self.assertEqual(x, 16.0) + self.assertIsInstance(y, float) + self.assertEqual(y, 16.0) + + f.size = 20.5, 22.25 + x, y = f.size + self.assertEqual(x, 20.5) + self.assertEqual(y, 22.25) + + f.size = 0, 0 + size = f.size + self.assertIsInstance(size, float) + self.assertEqual(size, 0.0) + self.assertRaises(ValueError, setattr, f, "size", (0, 24.0)) + self.assertRaises(TypeError, setattr, f, "size", (24.0,)) + self.assertRaises(TypeError, setattr, f, "size", (24.0, 0, 0)) + self.assertRaises(TypeError, setattr, f, "size", (24.0j, 24.0)) + self.assertRaises(TypeError, setattr, f, "size", (24.0, 24.0j)) + self.assertRaises(OverflowError, setattr, f, "size", (-1, 16)) + self.assertRaises(OverflowError, setattr, f, "size", (max_point_size + 1, 16)) + self.assertRaises(OverflowError, setattr, f, "size", (16, -1)) + self.assertRaises(OverflowError, setattr, f, "size", (16, max_point_size + 1)) + + # bitmap files with identical point size but differing ppems. + f75 = self._TEST_FONTS["bmp-18-75dpi"] + sizes = f75.get_sizes() + self.assertEqual(len(sizes), 1) + size_pt, width_px, height_px, x_ppem, y_ppem = sizes[0] + self.assertEqual(size_pt, 18) + self.assertEqual(x_ppem, 19.0) + self.assertEqual(y_ppem, 19.0) + rect = f75.get_rect("A", size=18) + rect = f75.get_rect("A", size=19) + rect = f75.get_rect("A", size=(19.0, 19.0)) + self.assertRaises(pygame.error, f75.get_rect, "A", size=17) + f100 = self._TEST_FONTS["bmp-18-100dpi"] + sizes = f100.get_sizes() + self.assertEqual(len(sizes), 1) + size_pt, width_px, height_px, x_ppem, y_ppem = sizes[0] + self.assertEqual(size_pt, 18) + self.assertEqual(x_ppem, 25.0) + self.assertEqual(y_ppem, 25.0) + rect = f100.get_rect("A", size=18) + rect = f100.get_rect("A", size=25) + rect = f100.get_rect("A", size=(25.0, 25.0)) + self.assertRaises(pygame.error, f100.get_rect, "A", size=17) + + def test_freetype_Font_rotation(self): + + test_angles = [ + (30, 30), + (360, 0), + (390, 30), + (720, 0), + (764, 44), + (-30, 330), + (-360, 0), + (-390, 330), + (-720, 0), + (-764, 316), + ] + + f = ft.Font(None) + self.assertEqual(f.rotation, 0) + for r, r_reduced in test_angles: + f.rotation = r + self.assertEqual( + f.rotation, + r_reduced, + "for angle %d: %d != %d" % (r, f.rotation, r_reduced), + ) + self.assertRaises(TypeError, setattr, f, "rotation", "12") + + def test_freetype_Font_render_to(self): + # Rendering to an existing target surface is equivalent to + # blitting a surface returned by Font.render with the target. + font = self._TEST_FONTS["sans"] + + surf = pygame.Surface((800, 600)) + color = pygame.Color(0, 0, 0) + + rrect = font.render_to(surf, (32, 32), "FoobarBaz", color, None, size=24) + self.assertIsInstance(rrect, pygame.Rect) + self.assertEqual(rrect.top, rrect.height) + ## self.assertEqual(rrect.left, something or other) + + rcopy = rrect.copy() + rcopy.topleft = (32, 32) + self.assertTrue(surf.get_rect().contains(rcopy)) + + rect = pygame.Rect(20, 20, 2, 2) + rrect = font.render_to(surf, rect, "FoobarBax", color, None, size=24) + self.assertEqual(rrect.top, rrect.height) + self.assertNotEqual(rrect.size, rect.size) + rrect = font.render_to(surf, (20.1, 18.9), "FoobarBax", color, None, size=24) + ## self.assertEqual(tuple(rend[1].topleft), (20, 18)) + + rrect = font.render_to(surf, rect, "", color, None, size=24) + self.assertFalse(rrect) + self.assertEqual(rrect.height, font.get_sized_height(24)) + + # invalid surf test + self.assertRaises(TypeError, font.render_to, "not a surface", "text", color) + self.assertRaises(TypeError, font.render_to, pygame.Surface, "text", color) + + # invalid dest test + for dest in [ + None, + 0, + "a", + "ab", + (), + (1,), + ("a", 2), + (1, "a"), + (1 + 2j, 2), + (1, 1 + 2j), + (1, int), + (int, 1), + ]: + self.assertRaises( + TypeError, font.render_to, surf, dest, "foobar", color, size=24 + ) + + # misc parameter test + self.assertRaises(ValueError, font.render_to, surf, (0, 0), "foobar", color) + self.assertRaises( + TypeError, font.render_to, surf, (0, 0), "foobar", color, 2.3, size=24 + ) + self.assertRaises( + ValueError, + font.render_to, + surf, + (0, 0), + "foobar", + color, + None, + style=42, + size=24, + ) + self.assertRaises( + TypeError, + font.render_to, + surf, + (0, 0), + "foobar", + color, + None, + style=None, + size=24, + ) + self.assertRaises( + ValueError, + font.render_to, + surf, + (0, 0), + "foobar", + color, + None, + style=97, + size=24, + ) + + def test_freetype_Font_render(self): + + font = self._TEST_FONTS["sans"] + + surf = pygame.Surface((800, 600)) + color = pygame.Color(0, 0, 0) + + rend = font.render("FoobarBaz", pygame.Color(0, 0, 0), None, size=24) + self.assertIsInstance(rend, tuple) + self.assertEqual(len(rend), 2) + self.assertIsInstance(rend[0], pygame.Surface) + self.assertIsInstance(rend[1], pygame.Rect) + self.assertEqual(rend[0].get_rect().size, rend[1].size) + + s, r = font.render("", pygame.Color(0, 0, 0), None, size=24) + self.assertEqual(r.width, 0) + self.assertEqual(r.height, font.get_sized_height(24)) + self.assertEqual(s.get_size(), r.size) + self.assertEqual(s.get_bitsize(), 32) + + # misc parameter test + self.assertRaises(ValueError, font.render, "foobar", color) + self.assertRaises(TypeError, font.render, "foobar", color, 2.3, size=24) + self.assertRaises( + ValueError, font.render, "foobar", color, None, style=42, size=24 + ) + self.assertRaises( + TypeError, font.render, "foobar", color, None, style=None, size=24 + ) + self.assertRaises( + ValueError, font.render, "foobar", color, None, style=97, size=24 + ) + + # valid surrogate pairs + font2 = self._TEST_FONTS["mono"] + ucs4 = font2.ucs4 + try: + font2.ucs4 = False + rend1 = font2.render(as_unicode(r"\uD80C\uDC79"), color, size=24) + rend2 = font2.render(as_unicode(r"\U00013079"), color, size=24) + self.assertEqual(rend1[1], rend2[1]) + font2.ucs4 = True + rend1 = font2.render(as_unicode(r"\uD80C\uDC79"), color, size=24) + self.assertNotEqual(rend1[1], rend2[1]) + finally: + font2.ucs4 = ucs4 + + # malformed surrogate pairs + self.assertRaises( + UnicodeEncodeError, font.render, as_unicode(r"\uD80C"), color, size=24 + ) + self.assertRaises( + UnicodeEncodeError, font.render, as_unicode(r"\uDCA7"), color, size=24 + ) + self.assertRaises( + UnicodeEncodeError, font.render, as_unicode(r"\uD7FF\uDCA7"), color, size=24 + ) + self.assertRaises( + UnicodeEncodeError, font.render, as_unicode(r"\uDC00\uDCA7"), color, size=24 + ) + self.assertRaises( + UnicodeEncodeError, font.render, as_unicode(r"\uD80C\uDBFF"), color, size=24 + ) + self.assertRaises( + UnicodeEncodeError, font.render, as_unicode(r"\uD80C\uE000"), color, size=24 + ) + + # raises exception when uninitalized + self.assertRaises(RuntimeError, nullfont().render, "a", (0, 0, 0), size=24) + + # Confirm the correct glpyhs are returned for a couple of + # unicode code points, 'A' and '\U00023079'. For each code point + # the rendered glyph is compared with an image of glyph bitmap + # as exported by FontForge. + path = os.path.join(FONTDIR, "A_PyGameMono-8.png") + A = pygame.image.load(path) + path = os.path.join(FONTDIR, "u13079_PyGameMono-8.png") + u13079 = pygame.image.load(path) + + font = self._TEST_FONTS["mono"] + font.ucs4 = False + A_rendered, r = font.render("A", bgcolor=pygame.Color("white"), size=8) + u13079_rendered, r = font.render( + as_unicode(r"\U00013079"), bgcolor=pygame.Color("white"), size=8 + ) + + ## before comparing the surfaces, make sure they are the same + ## pixel format. Use 32-bit SRCALPHA to avoid row padding and + ## undefined bytes (the alpha byte will be set to 255.) + bitmap = pygame.Surface(A.get_size(), pygame.SRCALPHA, 32) + bitmap.blit(A, (0, 0)) + rendering = pygame.Surface(A_rendered.get_size(), pygame.SRCALPHA, 32) + rendering.blit(A_rendered, (0, 0)) + self.assertTrue(surf_same_image(rendering, bitmap)) + bitmap = pygame.Surface(u13079.get_size(), pygame.SRCALPHA, 32) + bitmap.blit(u13079, (0, 0)) + rendering = pygame.Surface(u13079_rendered.get_size(), pygame.SRCALPHA, 32) + rendering.blit(u13079_rendered, (0, 0)) + self.assertTrue(surf_same_image(rendering, bitmap)) + + def test_freetype_Font_render_mono(self): + font = self._TEST_FONTS["sans"] + color = pygame.Color("black") + colorkey = pygame.Color("white") + text = "." + + save_antialiased = font.antialiased + font.antialiased = False + try: + surf, r = font.render(text, color, size=24) + self.assertEqual(surf.get_bitsize(), 8) + flags = surf.get_flags() + self.assertTrue(flags & pygame.SRCCOLORKEY) + self.assertFalse(flags & (pygame.SRCALPHA | pygame.HWSURFACE)) + self.assertEqual(surf.get_colorkey(), colorkey) + self.assertIsNone(surf.get_alpha()) + + translucent_color = pygame.Color(*color) + translucent_color.a = 55 + surf, r = font.render(text, translucent_color, size=24) + self.assertEqual(surf.get_bitsize(), 8) + flags = surf.get_flags() + self.assertTrue(flags & (pygame.SRCCOLORKEY | pygame.SRCALPHA)) + self.assertFalse(flags & pygame.HWSURFACE) + self.assertEqual(surf.get_colorkey(), colorkey) + self.assertEqual(surf.get_alpha(), translucent_color.a) + + surf, r = font.render(text, color, colorkey, size=24) + self.assertEqual(surf.get_bitsize(), 32) + finally: + font.antialiased = save_antialiased + + @unittest.skipIf( + pygame.get_sdl_version()[0] == 2, "skipping due to blending issue (#864)" + ) + def test_freetype_Font_render_to_mono(self): + # Blitting is done in two stages. First the target is alpha filled + # with the background color, if any. Second, the foreground + # color is alpha blitted to the background. + font = self._TEST_FONTS["sans"] + text = " ." + rect = font.get_rect(text, size=24) + size = rect.size + fg = pygame.Surface((1, 1), pygame.SRCALPHA, 32) + bg = pygame.Surface((1, 1), pygame.SRCALPHA, 32) + surrogate = pygame.Surface((1, 1), pygame.SRCALPHA, 32) + surfaces = [ + pygame.Surface(size, 0, 8), + pygame.Surface(size, 0, 16), + pygame.Surface(size, pygame.SRCALPHA, 16), + pygame.Surface(size, 0, 24), + pygame.Surface(size, 0, 32), + pygame.Surface(size, pygame.SRCALPHA, 32), + ] + fg_colors = [ + surfaces[0].get_palette_at(2), + surfaces[1].unmap_rgb(surfaces[1].map_rgb((128, 64, 200))), + surfaces[2].unmap_rgb(surfaces[2].map_rgb((99, 0, 100, 64))), + (128, 97, 213), + (128, 97, 213), + (128, 97, 213, 60), + ] + fg_colors = [pygame.Color(*c) for c in fg_colors] + self.assertEqual(len(surfaces), len(fg_colors)) # integrity check + bg_colors = [ + surfaces[0].get_palette_at(4), + surfaces[1].unmap_rgb(surfaces[1].map_rgb((220, 20, 99))), + surfaces[2].unmap_rgb(surfaces[2].map_rgb((55, 200, 0, 86))), + (255, 120, 13), + (255, 120, 13), + (255, 120, 13, 180), + ] + bg_colors = [pygame.Color(*c) for c in bg_colors] + self.assertEqual(len(surfaces), len(bg_colors)) # integrity check + + save_antialiased = font.antialiased + font.antialiased = False + try: + fill_color = pygame.Color("black") + for i, surf in enumerate(surfaces): + surf.fill(fill_color) + fg_color = fg_colors[i] + fg.set_at((0, 0), fg_color) + surf.blit(fg, (0, 0)) + r_fg_color = surf.get_at((0, 0)) + surf.set_at((0, 0), fill_color) + rrect = font.render_to(surf, (0, 0), text, fg_color, size=24) + bottomleft = 0, rrect.height - 1 + self.assertEqual( + surf.get_at(bottomleft), + fill_color, + "Position: {}. Depth: {}." + " fg_color: {}.".format(bottomleft, surf.get_bitsize(), fg_color), + ) + bottomright = rrect.width - 1, rrect.height - 1 + self.assertEqual( + surf.get_at(bottomright), + r_fg_color, + "Position: {}. Depth: {}." + " fg_color: {}.".format(bottomright, surf.get_bitsize(), fg_color), + ) + for i, surf in enumerate(surfaces): + surf.fill(fill_color) + fg_color = fg_colors[i] + bg_color = bg_colors[i] + bg.set_at((0, 0), bg_color) + fg.set_at((0, 0), fg_color) + if surf.get_bitsize() == 24: + # For a 24 bit target surface test against Pygame's alpha + # blit as there appears to be a problem with SDL's alpha + # blit: + # + # self.assertEqual(surf.get_at(bottomright), r_fg_color) + # + # raises + # + # AssertionError: (128, 97, 213, 255) != (129, 98, 213, 255) + # + surrogate.set_at((0, 0), fill_color) + surrogate.blit(bg, (0, 0)) + r_bg_color = surrogate.get_at((0, 0)) + surrogate.blit(fg, (0, 0)) + r_fg_color = surrogate.get_at((0, 0)) + else: + # Surface blit values for comparison. + surf.blit(bg, (0, 0)) + r_bg_color = surf.get_at((0, 0)) + surf.blit(fg, (0, 0)) + r_fg_color = surf.get_at((0, 0)) + surf.set_at((0, 0), fill_color) + rrect = font.render_to(surf, (0, 0), text, fg_color, bg_color, size=24) + bottomleft = 0, rrect.height - 1 + self.assertEqual(surf.get_at(bottomleft), r_bg_color) + bottomright = rrect.width - 1, rrect.height - 1 + self.assertEqual(surf.get_at(bottomright), r_fg_color) + finally: + font.antialiased = save_antialiased + + def test_freetype_Font_render_raw(self): + + font = self._TEST_FONTS["sans"] + + text = "abc" + size = font.get_rect(text, size=24).size + rend = font.render_raw(text, size=24) + self.assertIsInstance(rend, tuple) + self.assertEqual(len(rend), 2) + + r, s = rend + self.assertIsInstance(r, bytes_) + self.assertIsInstance(s, tuple) + self.assertTrue(len(s), 2) + + w, h = s + self.assertIsInstance(w, int) + self.assertIsInstance(h, int) + self.assertEqual(s, size) + self.assertEqual(len(r), w * h) + + r, (w, h) = font.render_raw("", size=24) + self.assertEqual(w, 0) + self.assertEqual(h, font.height) + self.assertEqual(len(r), 0) + + # bug with decenders: this would crash + rend = font.render_raw("render_raw", size=24) + + # bug with non-printable characters: this would cause a crash + # because the text length was not adjusted for skipped characters. + text = unicode_("").join([unichr_(i) for i in range(31, 64)]) + rend = font.render_raw(text, size=10) + + def test_freetype_Font_render_raw_to(self): + + # This only checks that blits do not crash. It needs to check: + # - int values + # - invert option + # + + font = self._TEST_FONTS["sans"] + text = "abc" + + # No frills antialiased render to int1 (__render_glyph_INT) + srect = font.get_rect(text, size=24) + surf = pygame.Surface(srect.size, 0, 8) + rrect = font.render_raw_to(surf.get_view("2"), text, size=24) + self.assertEqual(rrect, srect) + + for bpp in [24, 32]: + surf = pygame.Surface(srect.size, 0, bpp) + rrect = font.render_raw_to(surf.get_view("r"), text, size=24) + self.assertEqual(rrect, srect) + + # Underlining to int1 (__fill_glyph_INT) + srect = font.get_rect(text, size=24, style=ft.STYLE_UNDERLINE) + surf = pygame.Surface(srect.size, 0, 8) + rrect = font.render_raw_to( + surf.get_view("2"), text, size=24, style=ft.STYLE_UNDERLINE + ) + self.assertEqual(rrect, srect) + + for bpp in [24, 32]: + surf = pygame.Surface(srect.size, 0, bpp) + rrect = font.render_raw_to( + surf.get_view("r"), text, size=24, style=ft.STYLE_UNDERLINE + ) + self.assertEqual(rrect, srect) + + # Unaliased (mono) rendering to int1 (__render_glyph_MONO_as_INT) + font.antialiased = False + try: + srect = font.get_rect(text, size=24) + surf = pygame.Surface(srect.size, 0, 8) + rrect = font.render_raw_to(surf.get_view("2"), text, size=24) + self.assertEqual(rrect, srect) + + for bpp in [24, 32]: + surf = pygame.Surface(srect.size, 0, bpp) + rrect = font.render_raw_to(surf.get_view("r"), text, size=24) + self.assertEqual(rrect, srect) + finally: + font.antialiased = True + + # Antialiased render to ints sized greater than 1 byte + # (__render_glyph_INT) + srect = font.get_rect(text, size=24) + + for bpp in [16, 24, 32]: + surf = pygame.Surface(srect.size, 0, bpp) + rrect = font.render_raw_to(surf.get_view("2"), text, size=24) + self.assertEqual(rrect, srect) + + # Underline render to ints sized greater than 1 byte + # (__fill_glyph_INT) + srect = font.get_rect(text, size=24, style=ft.STYLE_UNDERLINE) + + for bpp in [16, 24, 32]: + surf = pygame.Surface(srect.size, 0, bpp) + rrect = font.render_raw_to( + surf.get_view("2"), text, size=24, style=ft.STYLE_UNDERLINE + ) + self.assertEqual(rrect, srect) + + # Unaliased (mono) rendering to ints greater than 1 byte + # (__render_glyph_MONO_as_INT) + font.antialiased = False + try: + srect = font.get_rect(text, size=24) + + for bpp in [16, 24, 32]: + surf = pygame.Surface(srect.size, 0, bpp) + rrect = font.render_raw_to(surf.get_view("2"), text, size=24) + self.assertEqual(rrect, srect) + finally: + font.antialiased = True + + # Invalid dest parameter test. + srect = font.get_rect(text, size=24) + surf_buf = pygame.Surface(srect.size, 0, 32).get_view("2") + + for dest in [ + 0, + "a", + "ab", + (), + (1,), + ("a", 2), + (1, "a"), + (1 + 2j, 2), + (1, 1 + 2j), + (1, int), + (int, 1), + ]: + self.assertRaises( + TypeError, font.render_raw_to, surf_buf, text, dest, size=24 + ) + + def test_freetype_Font_text_is_None(self): + f = ft.Font(self._sans_path, 36) + f.style = ft.STYLE_NORMAL + f.rotation = 0 + text = "ABCD" + + # reference values + get_rect = f.get_rect(text) + f.vertical = True + get_rect_vert = f.get_rect(text) + + self.assertTrue(get_rect_vert.width < get_rect.width) + self.assertTrue(get_rect_vert.height > get_rect.height) + f.vertical = False + render_to_surf = pygame.Surface(get_rect.size, pygame.SRCALPHA, 32) + + if IS_PYPY: + return + + arr = arrinter.Array(get_rect.size, "u", 1) + render = f.render(text, (0, 0, 0)) + render_to = f.render_to(render_to_surf, (0, 0), text, (0, 0, 0)) + render_raw = f.render_raw(text) + render_raw_to = f.render_raw_to(arr, text) + + # comparisons + surf = pygame.Surface(get_rect.size, pygame.SRCALPHA, 32) + self.assertEqual(f.get_rect(None), get_rect) + s, r = f.render(None, (0, 0, 0)) + self.assertEqual(r, render[1]) + self.assertTrue(surf_same_image(s, render[0])) + r = f.render_to(surf, (0, 0), None, (0, 0, 0)) + self.assertEqual(r, render_to) + self.assertTrue(surf_same_image(surf, render_to_surf)) + px, sz = f.render_raw(None) + self.assertEqual(sz, render_raw[1]) + self.assertEqual(px, render_raw[0]) + sz = f.render_raw_to(arr, None) + self.assertEqual(sz, render_raw_to) + + def test_freetype_Font_text_is_None(self): + f = ft.Font(self._sans_path, 36) + f.style = ft.STYLE_NORMAL + f.rotation = 0 + text = "ABCD" + + # reference values + get_rect = f.get_rect(text) + f.vertical = True + get_rect_vert = f.get_rect(text) + + # vertical: trigger glyph positioning. + f.vertical = True + r = f.get_rect(None) + self.assertEqual(r, get_rect_vert) + f.vertical = False + + # wide style: trigger glyph reload + r = f.get_rect(None, style=ft.STYLE_WIDE) + self.assertEqual(r.height, get_rect.height) + self.assertTrue(r.width > get_rect.width) + r = f.get_rect(None) + self.assertEqual(r, get_rect) + + # rotated: trigger glyph reload + r = f.get_rect(None, rotation=90) + self.assertEqual(r.width, get_rect.height) + self.assertEqual(r.height, get_rect.width) + + # this method will not support None text + self.assertRaises(TypeError, f.get_metrics, None) + + def test_freetype_Font_fgcolor(self): + f = ft.Font(self._bmp_8_75dpi_path) + notdef = "\0" # the PyGameMono .notdef glyph has a pixel at (0, 0) + f.origin = False + f.pad = False + black = pygame.Color("black") # initial color + green = pygame.Color("green") + alpha128 = pygame.Color(10, 20, 30, 128) + + c = f.fgcolor + self.assertIsInstance(c, pygame.Color) + self.assertEqual(c, black) + + s, r = f.render(notdef) + self.assertEqual(s.get_at((0, 0)), black) + + f.fgcolor = green + self.assertEqual(f.fgcolor, green) + + s, r = f.render(notdef) + self.assertEqual(s.get_at((0, 0)), green) + + f.fgcolor = alpha128 + s, r = f.render(notdef) + self.assertEqual(s.get_at((0, 0)), alpha128) + + surf = pygame.Surface(f.get_rect(notdef).size, pygame.SRCALPHA, 32) + f.render_to(surf, (0, 0), None) + self.assertEqual(surf.get_at((0, 0)), alpha128) + + self.assertRaises(AttributeError, setattr, f, "fgcolor", None) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_newbuf(self): + from pygame.tests.test_utils import buftools + + Exporter = buftools.Exporter + font = self._TEST_FONTS["sans"] + srect = font.get_rect("Hi", size=12) + for format in [ + "b", + "B", + "h", + "H", + "i", + "I", + "l", + "L", + "q", + "Q", + "x", + "1x", + "2x", + "3x", + "4x", + "5x", + "6x", + "7x", + "8x", + "9x", + "h", + "=h", + "@h", + "!h", + "1h", + "=1h", + ]: + newbuf = Exporter(srect.size, format=format) + rrect = font.render_raw_to(newbuf, "Hi", size=12) + self.assertEqual(rrect, srect) + # Some unsupported formats + for format in ["f", "d", "2h", "?", "hh"]: + newbuf = Exporter(srect.size, format=format, itemsize=4) + self.assertRaises(ValueError, font.render_raw_to, newbuf, "Hi", size=12) + + def test_freetype_Font_style(self): + + font = self._TEST_FONTS["sans"] + + # make sure STYLE_NORMAL is the default value + self.assertEqual(ft.STYLE_NORMAL, font.style) + + # make sure we check for style type + with self.assertRaises(TypeError): + font.style = "None" + with self.assertRaises(TypeError): + font.style = None + + # make sure we only accept valid constants + with self.assertRaises(ValueError): + font.style = 112 + + # make assure no assignments happened + self.assertEqual(ft.STYLE_NORMAL, font.style) + + # test assignement + font.style = ft.STYLE_UNDERLINE + self.assertEqual(ft.STYLE_UNDERLINE, font.style) + + # test complex styles + st = ft.STYLE_STRONG | ft.STYLE_UNDERLINE | ft.STYLE_OBLIQUE + + font.style = st + self.assertEqual(st, font.style) + + # and that STYLE_DEFAULT has no effect (continued from above) + self.assertNotEqual(st, ft.STYLE_DEFAULT) + font.style = ft.STYLE_DEFAULT + self.assertEqual(st, font.style) + + # revert changes + font.style = ft.STYLE_NORMAL + self.assertEqual(ft.STYLE_NORMAL, font.style) + + def test_freetype_Font_resolution(self): + text = "|" # Differs in width and height + resolution = ft.get_default_resolution() + new_font = ft.Font(self._sans_path, resolution=2 * resolution) + self.assertEqual(new_font.resolution, 2 * resolution) + size_normal = self._TEST_FONTS["sans"].get_rect(text, size=24).size + size_scaled = new_font.get_rect(text, size=24).size + size_by_2 = size_normal[0] * 2 + self.assertTrue( + size_by_2 + 2 >= size_scaled[0] >= size_by_2 - 2, + "%i not equal %i" % (size_scaled[1], size_by_2), + ) + size_by_2 = size_normal[1] * 2 + self.assertTrue( + size_by_2 + 2 >= size_scaled[1] >= size_by_2 - 2, + "%i not equal %i" % (size_scaled[1], size_by_2), + ) + new_resolution = resolution + 10 + ft.set_default_resolution(new_resolution) + try: + new_font = ft.Font(self._sans_path, resolution=0) + self.assertEqual(new_font.resolution, new_resolution) + finally: + ft.set_default_resolution() + + def test_freetype_Font_path(self): + self.assertEqual(self._TEST_FONTS["sans"].path, self._sans_path) + self.assertRaises(AttributeError, getattr, nullfont(), "path") + + # This Font cache test is conditional on freetype being built by a debug + # version of Python or with the C macro PGFT_DEBUG_CACHE defined. + def test_freetype_Font_cache(self): + glyphs = "abcde" + glen = len(glyphs) + other_glyphs = "123" + oglen = len(other_glyphs) + uempty = unicode_("") + ## many_glyphs = (uempty.join([unichr_(i) for i in range(32,127)] + + ## [unichr_(i) for i in range(161,172)] + + ## [unichr_(i) for i in range(174,239)])) + many_glyphs = uempty.join([unichr_(i) for i in range(32, 127)]) + mglen = len(many_glyphs) + + count = 0 + access = 0 + hit = 0 + miss = 0 + + f = ft.Font(None, size=24, font_index=0, resolution=72, ucs4=False) + f.style = ft.STYLE_NORMAL + f.antialiased = True + + # Ensure debug counters are zero + self.assertEqual(f._debug_cache_stats, (0, 0, 0, 0, 0)) + # Load some basic glyphs + count = access = miss = glen + f.render_raw(glyphs) + self.assertEqual(f._debug_cache_stats, (count, 0, access, hit, miss)) + # Vertical should not affect the cache + access += glen + hit += glen + f.vertical = True + f.render_raw(glyphs) + f.vertical = False + self.assertEqual(f._debug_cache_stats, (count, 0, access, hit, miss)) + # New glyphs will + count += oglen + access += oglen + miss += oglen + f.render_raw(other_glyphs) + self.assertEqual(f._debug_cache_stats, (count, 0, access, hit, miss)) + # Point size does + count += glen + access += glen + miss += glen + f.render_raw(glyphs, size=12) + self.assertEqual(f._debug_cache_stats, (count, 0, access, hit, miss)) + # Underline style does not + access += oglen + hit += oglen + f.underline = True + f.render_raw(other_glyphs) + f.underline = False + self.assertEqual(f._debug_cache_stats, (count, 0, access, hit, miss)) + # Oblique style does + count += glen + access += glen + miss += glen + f.oblique = True + f.render_raw(glyphs) + f.oblique = False + self.assertEqual(f._debug_cache_stats, (count, 0, access, hit, miss)) + # Strong style does; by this point cache clears can happen + count += glen + access += glen + miss += glen + f.strong = True + f.render_raw(glyphs) + f.strong = False + ccount, cdelete_count, caccess, chit, cmiss = f._debug_cache_stats + self.assertEqual( + (ccount + cdelete_count, caccess, chit, cmiss), (count, access, hit, miss) + ) + # Rotation does + count += glen + access += glen + miss += glen + f.render_raw(glyphs, rotation=10) + ccount, cdelete_count, caccess, chit, cmiss = f._debug_cache_stats + self.assertEqual( + (ccount + cdelete_count, caccess, chit, cmiss), (count, access, hit, miss) + ) + # aliased (mono) glyphs do + count += oglen + access += oglen + miss += oglen + f.antialiased = False + f.render_raw(other_glyphs) + f.antialiased = True + ccount, cdelete_count, caccess, chit, cmiss = f._debug_cache_stats + self.assertEqual( + (ccount + cdelete_count, caccess, chit, cmiss), (count, access, hit, miss) + ) + # Trigger a cleanup for sure. + count += 2 * mglen + access += 2 * mglen + miss += 2 * mglen + f.get_metrics(many_glyphs, size=8) + f.get_metrics(many_glyphs, size=10) + ccount, cdelete_count, caccess, chit, cmiss = f._debug_cache_stats + self.assertTrue(ccount < count) + self.assertEqual( + (ccount + cdelete_count, caccess, chit, cmiss), (count, access, hit, miss) + ) + + try: + ft.Font._debug_cache_stats + except AttributeError: + del test_freetype_Font_cache + + def test_undefined_character_code(self): + # To be consistent with pygame.font.Font, undefined codes + # are rendered as the undefined character, and has metrics + # of None. + font = self._TEST_FONTS["sans"] + + img, size1 = font.render(unichr_(1), (0, 0, 0), size=24) + img, size0 = font.render("", (0, 0, 0), size=24) + self.assertTrue(size1.width > size0.width) + + metrics = font.get_metrics(unichr_(1) + unichr_(48), size=24) + self.assertEqual(len(metrics), 2) + self.assertIsNone(metrics[0]) + self.assertIsInstance(metrics[1], tuple) + + @unittest.skipIf( + pygame.get_sdl_version()[0] == 2, "SDL2 surfaces are only limited by memory" + ) + def test_issue_144(self): + """Issue #144: unable to render text""" + + # The bug came in two parts. The first was a convertion bug from + # FT_Fixed to integer in for an Intel x86_64 Pygame build. The second + # was to have the raised exception disappear before Font.render + # returned to Python level. + # + font = ft.Font(None, size=64) + s = "M" * 100000 # Way too long for an SDL surface + self.assertRaises(pygame.error, font.render, s, (0, 0, 0)) + + def test_issue_242(self): + """Issue #242: get_rect() uses 0 as default style""" + + # Issue #242: freetype.Font.get_rect() ignores style defaults when + # the style argument is not given + # + # The text boundary rectangle returned by freetype.Font.get_rect() + # should match the boundary of the same text rendered directly to a + # surface. This permits accurate text positioning. To work properly, + # get_rect() should calculate the text boundary to reflect text style, + # such as underline. Instead, it ignores the style settings for the + # Font object when the style argument is omitted. + # + # When the style argument is not given, freetype.get_rect() uses + # unstyled text when calculating the boundary rectangle. This is + # because _ftfont_getrect(), in _freetype.c, set the default + # style to 0 rather than FT_STYLE_DEFAULT. + # + font = self._TEST_FONTS["sans"] + + # Try wide style on a wide character. + prev_style = font.wide + font.wide = True + try: + rect = font.get_rect("M", size=64) + surf, rrect = font.render(None, size=64) + self.assertEqual(rect, rrect) + finally: + font.wide = prev_style + + # Try strong style on several wide characters. + prev_style = font.strong + font.strong = True + try: + rect = font.get_rect("Mm_", size=64) + surf, rrect = font.render(None, size=64) + self.assertEqual(rect, rrect) + finally: + font.strong = prev_style + + # Try oblique style on a tall, narrow character. + prev_style = font.oblique + font.oblique = True + try: + rect = font.get_rect("|", size=64) + surf, rrect = font.render(None, size=64) + self.assertEqual(rect, rrect) + finally: + font.oblique = prev_style + + # Try underline style on a glyphless character. + prev_style = font.underline + font.underline = True + try: + rect = font.get_rect(" ", size=64) + surf, rrect = font.render(None, size=64) + self.assertEqual(rect, rrect) + finally: + font.underline = prev_style + + def test_issue_237(self): + """Issue #237: Memory overrun when rendered with underlining""" + + # Issue #237: Memory overrun when text without descenders is rendered + # with underlining + # + # The bug crashes the Python interpreter. The bug is caught with C + # assertions in ft_render_cb.c when the Pygame module is compiled + # for debugging. So far it is only known to affect Times New Roman. + # + name = "Times New Roman" + font = ft.SysFont(name, 19) + if font.name != name: + # The font is unavailable, so skip the test. + return + font.underline = True + s, r = font.render("Amazon", size=19) + + # Some other checks to make sure nothing else broke. + for adj in [-2, -1.9, -1, 0, 1.9, 2]: + font.underline_adjustment = adj + s, r = font.render("Amazon", size=19) + + def test_issue_243(self): + """Issue Y: trailing space ignored in boundary calculation""" + + # Issue #243: For a string with trailing spaces, freetype ignores the + # last space in boundary calculations + # + font = self._TEST_FONTS["fixed"] + r1 = font.get_rect(" ", size=64) + self.assertTrue(r1.width > 1) + r2 = font.get_rect(" ", size=64) + self.assertEqual(r2.width, 2 * r1.width) + + def test_garbage_collection(self): + """Check reference counting on returned new references""" + + def ref_items(seq): + return [weakref.ref(o) for o in seq] + + font = self._TEST_FONTS["bmp-8-75dpi"] + font.size = font.get_sizes()[0][0] + text = "A" + rect = font.get_rect(text) + surf = pygame.Surface(rect.size, pygame.SRCALPHA, 32) + refs = [] + refs.extend(ref_items(font.render(text, (0, 0, 0)))) + refs.append(weakref.ref(font.render_to(surf, (0, 0), text, (0, 0, 0)))) + refs.append(weakref.ref(font.get_rect(text))) + + n = len(refs) + self.assertTrue(n > 0) + + # for pypy we garbage collection twice. + for i in range(2): + gc.collect() + + for i in range(n): + self.assertIsNone(refs[i](), "ref %d not collected" % i) + + try: + from sys import getrefcount + except ImportError: + pass + else: + array = arrinter.Array(rect.size, "u", 1) + o = font.render_raw(text) + self.assertEqual(getrefcount(o), 2) + self.assertEqual(getrefcount(o[0]), 2) + self.assertEqual(getrefcount(o[1]), 2) + self.assertEqual(getrefcount(font.render_raw_to(array, text)), 1) + o = font.get_metrics("AB") + self.assertEqual(getrefcount(o), 2) + for i in range(len(o)): + self.assertEqual(getrefcount(o[i]), 2, "refcount fail for item %d" % i) + o = font.get_sizes() + self.assertEqual(getrefcount(o), 2) + for i in range(len(o)): + self.assertEqual(getrefcount(o[i]), 2, "refcount fail for item %d" % i) + + def test_display_surface_quit(self): + """Font.render_to() on a closed display surface""" + + # The Font.render_to() method checks that PySurfaceObject.surf is NULL + # and raise a exception if it is. This fixes a bug in Pygame revision + # 0600ea4f1cfb and earlier where Pygame segfaults instead. + null_surface = pygame.Surface.__new__(pygame.Surface) + f = self._TEST_FONTS["sans"] + self.assertRaises( + pygame.error, f.render_to, null_surface, (0, 0), "Crash!", size=12 + ) + + def test_issue_565(self): + """get_metrics supporting rotation/styles/size""" + + tests = [ + {"method": "size", "value": 36, "msg": "metrics same for size"}, + {"method": "rotation", "value": 90, "msg": "metrics same for rotation"}, + {"method": "oblique", "value": True, "msg": "metrics same for oblique"}, + ] + text = "|" + + def run_test(method, value, msg): + font = ft.Font(self._sans_path, size=24) + before = font.get_metrics(text) + font.__setattr__(method, value) + after = font.get_metrics(text) + self.assertNotEqual(before, after, msg) + + for test in tests: + run_test(test["method"], test["value"], test["msg"]) + + +class FreeTypeTest(unittest.TestCase): + def setUp(self): + ft.init() + + def tearDown(self): + ft.quit() + + def test_resolution(self): + try: + ft.set_default_resolution() + resolution = ft.get_default_resolution() + self.assertEqual(resolution, 72) + new_resolution = resolution + 10 + ft.set_default_resolution(new_resolution) + self.assertEqual(ft.get_default_resolution(), new_resolution) + ft.init(resolution=resolution + 20) + self.assertEqual(ft.get_default_resolution(), new_resolution) + finally: + ft.set_default_resolution() + + def test_autoinit_and_autoquit(self): + pygame.init() + self.assertTrue(ft.get_init()) + pygame.quit() + self.assertFalse(ft.get_init()) + + # Ensure autoquit is replaced at init time + pygame.init() + self.assertTrue(ft.get_init()) + pygame.quit() + self.assertFalse(ft.get_init()) + + def test_init(self): + # Test if module initialized after calling init(). + ft.quit() + ft.init() + + self.assertTrue(ft.get_init()) + + def test_init__multiple(self): + # Test if module initialized after multiple init() calls. + ft.init() + ft.init() + + self.assertTrue(ft.get_init()) + + def test_quit(self): + # Test if module uninitialized after calling quit(). + ft.quit() + + self.assertFalse(ft.get_init()) + + def test_quit__multiple(self): + # Test if module initialized after multiple quit() calls. + ft.quit() + ft.quit() + + self.assertFalse(ft.get_init()) + + def test_get_init(self): + # Test if get_init() gets the init state. + self.assertTrue(ft.get_init()) + + def test_cache_size(self): + DEFAULT_CACHE_SIZE = 64 + self.assertEqual(ft.get_cache_size(), DEFAULT_CACHE_SIZE) + ft.quit() + self.assertEqual(ft.get_cache_size(), 0) + new_cache_size = DEFAULT_CACHE_SIZE * 2 + ft.init(cache_size=new_cache_size) + self.assertEqual(ft.get_cache_size(), new_cache_size) + + def test_get_error(self): + """Ensures get_error() is initially empty (None).""" + error_msg = ft.get_error() + + self.assertIsNone(error_msg) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/ftfont_tags.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/ftfont_tags.py new file mode 100644 index 0000000..0d538f4 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/ftfont_tags.py @@ -0,0 +1,11 @@ +__tags__ = ["development"] + +exclude = False + +try: + import pygame.ftfont +except ImportError: + exclude = True + +if exclude: + __tags__.extend(["ignore", "subprocess_ignore"]) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/ftfont_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/ftfont_test.py new file mode 100644 index 0000000..1f71204 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/ftfont_test.py @@ -0,0 +1,19 @@ +import sys +import os +import unittest +from pygame.tests import font_test + +import pygame.ftfont + +font_test.pygame_font = pygame.ftfont +# Disable UCS-4 specific tests as this "Font" type does accept UCS-4 codes. +font_test.UCS_4 = False + +for name in dir(font_test): + obj = getattr(font_test, name) + if isinstance(obj, type) and issubclass(obj, unittest.TestCase): # conditional and + new_name = "Ft%s" % name + globals()[new_name] = type(new_name, (obj,), {}) + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/gfxdraw_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/gfxdraw_test.py new file mode 100644 index 0000000..bfa9921 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/gfxdraw_test.py @@ -0,0 +1,879 @@ +import unittest +import pygame +import pygame.gfxdraw +from pygame.locals import * +from pygame.tests.test_utils import SurfaceSubclass + + +def intensity(c, i): + """Return color c changed by intensity i + + For 0 <= i <= 127 the color is a shade, with 0 being black, 127 being the + unaltered color. + + For 128 <= i <= 255 the color is a tint, with 255 being white, 128 the + unaltered color. + + """ + r, g, b = c[0:3] + if 0 <= i <= 127: + # Darken + return ((r * i) // 127, (g * i) // 127, (b * i) // 127) + # Lighten + return ( + r + ((255 - r) * (255 - i)) // 127, + g + ((255 - g) * (255 - i)) // 127, + b + ((255 - b) * (255 - i)) // 127, + ) + + +class GfxdrawDefaultTest(unittest.TestCase): + + is_started = False + + foreground_color = (128, 64, 8) + background_color = (255, 255, 255) + + def make_palette(base_color): + """Return color palette that is various intensities of base_color""" + # Need this function for Python 3.x so the base_color + # is within the scope of the list comprehension. + return [intensity(base_color, i) for i in range(0, 256)] + + default_palette = make_palette(foreground_color) + + default_size = (100, 100) + + def check_at(self, surf, posn, color): + sc = surf.get_at(posn) + fail_msg = "%s != %s at %s, bitsize: %i, flags: %i, masks: %s" % ( + sc, + color, + posn, + surf.get_bitsize(), + surf.get_flags(), + surf.get_masks(), + ) + self.assertEqual(sc, color, fail_msg) + + def check_not_at(self, surf, posn, color): + sc = surf.get_at(posn) + fail_msg = "%s != %s at %s, bitsize: %i, flags: %i, masks: %s" % ( + sc, + color, + posn, + surf.get_bitsize(), + surf.get_flags(), + surf.get_masks(), + ) + self.assertNotEqual(sc, color, fail_msg) + + @classmethod + def setUpClass(cls): + # Necessary for Surface.set_palette. + pygame.init() + pygame.display.set_mode((1, 1)) + + @classmethod + def tearDownClass(cls): + pygame.quit() + + def setUp(self): + # This makes sure pygame is always initialized before each test (in + # case a test calls pygame.quit()). + if not pygame.get_init(): + pygame.init() + + Surface = pygame.Surface + size = self.default_size + palette = self.default_palette + if not self.is_started: + # Create test surfaces + self.surfaces = [ + Surface(size, 0, 8), + Surface(size, SRCALPHA, 16), + Surface(size, SRCALPHA, 32), + ] + self.surfaces[0].set_palette(palette) + nonpalette_fmts = ( + # (8, (0xe0, 0x1c, 0x3, 0x0)), + (12, (0xF00, 0xF0, 0xF, 0x0)), + (15, (0x7C00, 0x3E0, 0x1F, 0x0)), + (15, (0x1F, 0x3E0, 0x7C00, 0x0)), + (16, (0xF00, 0xF0, 0xF, 0xF000)), + (16, (0xF000, 0xF00, 0xF0, 0xF)), + (16, (0xF, 0xF0, 0xF00, 0xF000)), + (16, (0xF0, 0xF00, 0xF000, 0xF)), + (16, (0x7C00, 0x3E0, 0x1F, 0x8000)), + (16, (0xF800, 0x7C0, 0x3E, 0x1)), + (16, (0x1F, 0x3E0, 0x7C00, 0x8000)), + (16, (0x3E, 0x7C0, 0xF800, 0x1)), + (16, (0xF800, 0x7E0, 0x1F, 0x0)), + (16, (0x1F, 0x7E0, 0xF800, 0x0)), + (24, (0xFF, 0xFF00, 0xFF0000, 0x0)), + (24, (0xFF0000, 0xFF00, 0xFF, 0x0)), + (32, (0xFF0000, 0xFF00, 0xFF, 0x0)), + (32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)), + (32, (0xFF, 0xFF00, 0xFF0000, 0x0)), + (32, (0xFF00, 0xFF0000, 0xFF000000, 0x0)), + (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)), + (32, (0xFF000000, 0xFF0000, 0xFF00, 0xFF)), + (32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)), + (32, (0xFF00, 0xFF0000, 0xFF000000, 0xFF)), + ) + for bitsize, masks in nonpalette_fmts: + self.surfaces.append(Surface(size, 0, bitsize, masks)) + for surf in self.surfaces: + surf.fill(self.background_color) + + def test_gfxdraw__subclassed_surface(self): + """Ensure pygame.gfxdraw works on subclassed surfaces.""" + surface = SurfaceSubclass((11, 13), SRCALPHA, 32) + surface.fill(pygame.Color("blue")) + expected_color = pygame.Color("red") + x, y = 1, 2 + + pygame.gfxdraw.pixel(surface, x, y, expected_color) + + self.assertEqual(surface.get_at((x, y)), expected_color) + + def test_pixel(self): + """pixel(surface, x, y, color): return None""" + fg = self.foreground_color + bg = self.background_color + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.pixel(surf, 2, 2, fg) + for x in range(1, 4): + for y in range(1, 4): + if x == 2 and y == 2: + self.check_at(surf, (x, y), fg_adjusted) + else: + self.check_at(surf, (x, y), bg_adjusted) + + def test_hline(self): + """hline(surface, x1, x2, y, color): return None""" + fg = self.foreground_color + bg = self.background_color + startx = 10 + stopx = 80 + y = 50 + fg_test_points = [(startx, y), (stopx, y), ((stopx - startx) // 2, y)] + bg_test_points = [ + (startx - 1, y), + (stopx + 1, y), + (startx, y - 1), + (startx, y + 1), + (stopx, y - 1), + (stopx, y + 1), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.hline(surf, startx, stopx, y, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_vline(self): + """vline(surface, x, y1, y2, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 50 + starty = 10 + stopy = 80 + fg_test_points = [(x, starty), (x, stopy), (x, (stopy - starty) // 2)] + bg_test_points = [ + (x, starty - 1), + (x, stopy + 1), + (x - 1, starty), + (x + 1, starty), + (x - 1, stopy), + (x + 1, stopy), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.vline(surf, x, starty, stopy, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_rectangle(self): + """rectangle(surface, rect, color): return None""" + fg = self.foreground_color + bg = self.background_color + rect = pygame.Rect(10, 15, 55, 62) + rect_tuple = tuple(rect) + fg_test_points = [ + rect.topleft, + (rect.right - 1, rect.top), + (rect.left, rect.bottom - 1), + (rect.right - 1, rect.bottom - 1), + ] + bg_test_points = [ + (rect.left - 1, rect.top - 1), + (rect.left + 1, rect.top + 1), + (rect.right, rect.top - 1), + (rect.right - 2, rect.top + 1), + (rect.left - 1, rect.bottom), + (rect.left + 1, rect.bottom - 2), + (rect.right, rect.bottom), + (rect.right - 2, rect.bottom - 2), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.rectangle(surf, rect, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + surf.fill(bg) + pygame.gfxdraw.rectangle(surf, rect_tuple, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_box(self): + """box(surface, rect, color): return None""" + fg = self.foreground_color + bg = self.background_color + rect = pygame.Rect(10, 15, 55, 62) + rect_tuple = tuple(rect) + fg_test_points = [ + rect.topleft, + (rect.left + 1, rect.top + 1), + (rect.right - 1, rect.top), + (rect.right - 2, rect.top + 1), + (rect.left, rect.bottom - 1), + (rect.left + 1, rect.bottom - 2), + (rect.right - 1, rect.bottom - 1), + (rect.right - 2, rect.bottom - 2), + ] + bg_test_points = [ + (rect.left - 1, rect.top - 1), + (rect.right, rect.top - 1), + (rect.left - 1, rect.bottom), + (rect.right, rect.bottom), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.box(surf, rect, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + surf.fill(bg) + pygame.gfxdraw.box(surf, rect_tuple, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_line(self): + """line(surface, x1, y1, x2, y2, color): return None""" + fg = self.foreground_color + bg = self.background_color + x1 = 10 + y1 = 15 + x2 = 92 + y2 = 77 + fg_test_points = [(x1, y1), (x2, y2)] + bg_test_points = [ + (x1 - 1, y1), + (x1, y1 - 1), + (x1 - 1, y1 - 1), + (x2 + 1, y2), + (x2, y2 + 1), + (x2 + 1, y2 + 1), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.line(surf, x1, y1, x2, y2, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_circle(self): + """circle(surface, x, y, r, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 45 + y = 40 + r = 30 + fg_test_points = [(x, y - r), (x, y + r), (x - r, y), (x + r, y)] + bg_test_points = [ + (x, y), + (x, y - r + 1), + (x, y - r - 1), + (x, y + r + 1), + (x, y + r - 1), + (x - r - 1, y), + (x - r + 1, y), + (x + r + 1, y), + (x + r - 1, y), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.circle(surf, x, y, r, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_arc(self): + """arc(surface, x, y, r, start, end, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 45 + y = 40 + r = 30 + start = 0 # +x direction, but not (x + r, y) (?) + end = 90 # -y direction, including (x, y + r) + fg_test_points = [(x, y + r), (x + r, y + 1)] + bg_test_points = [ + (x, y), + (x, y - r), + (x - r, y), + (x, y + r + 1), + (x, y + r - 1), + (x - 1, y + r), + (x + r + 1, y), + (x + r - 1, y), + (x + r, y), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.arc(surf, x, y, r, start, end, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_aacircle(self): + """aacircle(surface, x, y, r, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 45 + y = 40 + r = 30 + fg_test_points = [(x, y - r), (x, y + r), (x - r, y), (x + r, y)] + bg_test_points = [ + (x, y), + (x, y - r + 1), + (x, y - r - 1), + (x, y + r + 1), + (x, y + r - 1), + (x - r - 1, y), + (x - r + 1, y), + (x + r + 1, y), + (x + r - 1, y), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.aacircle(surf, x, y, r, fg) + for posn in fg_test_points: + self.check_not_at(surf, posn, bg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_filled_circle(self): + """filled_circle(surface, x, y, r, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 45 + y = 40 + r = 30 + fg_test_points = [ + (x, y - r), + (x, y - r + 1), + (x, y + r), + (x, y + r - 1), + (x - r, y), + (x - r + 1, y), + (x + r, y), + (x + r - 1, y), + (x, y), + ] + bg_test_points = [ + (x, y - r - 1), + (x, y + r + 1), + (x - r - 1, y), + (x + r + 1, y), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.filled_circle(surf, x, y, r, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_ellipse(self): + """ellipse(surface, x, y, rx, ry, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 45 + y = 40 + rx = 30 + ry = 35 + fg_test_points = [(x, y - ry), (x, y + ry), (x - rx, y), (x + rx, y)] + bg_test_points = [ + (x, y), + (x, y - ry + 1), + (x, y - ry - 1), + (x, y + ry + 1), + (x, y + ry - 1), + (x - rx - 1, y), + (x - rx + 1, y), + (x + rx + 1, y), + (x + rx - 1, y), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.ellipse(surf, x, y, rx, ry, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_aaellipse(self): + """aaellipse(surface, x, y, rx, ry, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 45 + y = 40 + rx = 30 + ry = 35 + fg_test_points = [(x, y - ry), (x, y + ry), (x - rx, y), (x + rx, y)] + bg_test_points = [ + (x, y), + (x, y - ry + 1), + (x, y - ry - 1), + (x, y + ry + 1), + (x, y + ry - 1), + (x - rx - 1, y), + (x - rx + 1, y), + (x + rx + 1, y), + (x + rx - 1, y), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.aaellipse(surf, x, y, rx, ry, fg) + for posn in fg_test_points: + self.check_not_at(surf, posn, bg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_filled_ellipse(self): + """filled_ellipse(surface, x, y, rx, ry, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 45 + y = 40 + rx = 30 + ry = 35 + fg_test_points = [ + (x, y - ry), + (x, y - ry + 1), + (x, y + ry), + (x, y + ry - 1), + (x - rx, y), + (x - rx + 1, y), + (x + rx, y), + (x + rx - 1, y), + (x, y), + ] + bg_test_points = [ + (x, y - ry - 1), + (x, y + ry + 1), + (x - rx - 1, y), + (x + rx + 1, y), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.filled_ellipse(surf, x, y, rx, ry, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_pie(self): + """pie(surface, x, y, r, start, end, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 45 + y = 40 + r = 30 + start = 0 # +x direction, including (x + r, y) + end = 90 # -y direction, but not (x, y + r) (?) + fg_test_points = [(x, y), (x + 1, y), (x, y + 1), (x + r, y)] + bg_test_points = [ + (x - 1, y), + (x, y - 1), + (x - 1, y - 1), + (x + 1, y + 1), + (x + r + 1, y), + (x + r, y - 1), + (x, y + r + 1), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.pie(surf, x, y, r, start, end, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_trigon(self): + """trigon(surface, x1, y1, x2, y2, x3, y3, color): return None""" + fg = self.foreground_color + bg = self.background_color + x1 = 10 + y1 = 15 + x2 = 92 + y2 = 77 + x3 = 20 + y3 = 60 + fg_test_points = [(x1, y1), (x2, y2), (x3, y3)] + bg_test_points = [ + (x1 - 1, y1 - 1), + (x2 + 1, y2 + 1), + (x3 - 1, y3 + 1), + (x1 + 10, y1 + 30), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.trigon(surf, x1, y1, x2, y2, x3, y3, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_aatrigon(self): + """aatrigon(surface, x1, y1, x2, y2, x3, y3, color): return None""" + fg = self.foreground_color + bg = self.background_color + x1 = 10 + y1 = 15 + x2 = 92 + y2 = 77 + x3 = 20 + y3 = 60 + fg_test_points = [(x1, y1), (x2, y2), (x3, y3)] + bg_test_points = [ + (x1 - 1, y1 - 1), + (x2 + 1, y2 + 1), + (x3 - 1, y3 + 1), + (x1 + 10, y1 + 30), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.aatrigon(surf, x1, y1, x2, y2, x3, y3, fg) + for posn in fg_test_points: + self.check_not_at(surf, posn, bg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + @unittest.expectedFailure + def test_aatrigon__with_horizontal_edge(self): + """Ensure aatrigon draws horizontal edges correctly. + + This test creates 2 surfaces and draws an aatrigon on each. The pixels + on each surface are compared to ensure they are the same. The only + difference between the 2 aatrigons is the order the points are drawn. + The order of the points should have no impact on the final drawing. + + Related to issue #622. + """ + bg_color = pygame.Color("white") + line_color = pygame.Color("black") + width, height = 11, 10 + expected_surface = pygame.Surface((width, height), 0, 32) + expected_surface.fill(bg_color) + surface = pygame.Surface((width, height), 0, 32) + surface.fill(bg_color) + + x1, y1 = width - 1, 0 + x2, y2 = (width - 1) // 2, height - 1 + x3, y3 = 0, 0 + + # The points in this order draw as expected. + pygame.gfxdraw.aatrigon(expected_surface, x1, y1, x2, y2, x3, y3, line_color) + + # The points in reverse order fail to draw the horizontal edge along + # the top. + pygame.gfxdraw.aatrigon(surface, x3, y3, x2, y2, x1, y1, line_color) + + # The surfaces are locked for a possible speed up of pixel access. + expected_surface.lock() + surface.lock() + for x in range(width): + for y in range(height): + self.assertEqual( + expected_surface.get_at((x, y)), + surface.get_at((x, y)), + "pos=({}, {})".format(x, y), + ) + + surface.unlock() + expected_surface.unlock() + + def test_filled_trigon(self): + """filled_trigon(surface, x1, y1, x2, y2, x3, y3, color): return None""" + fg = self.foreground_color + bg = self.background_color + x1 = 10 + y1 = 15 + x2 = 92 + y2 = 77 + x3 = 20 + y3 = 60 + fg_test_points = [(x1, y1), (x2, y2), (x3, y3), (x1 + 10, y1 + 30)] + bg_test_points = [(x1 - 1, y1 - 1), (x2 + 1, y2 + 1), (x3 - 1, y3 + 1)] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.filled_trigon(surf, x1, y1, x2, y2, x3, y3, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_polygon(self): + """polygon(surface, points, color): return None""" + fg = self.foreground_color + bg = self.background_color + points = [(10, 80), (10, 15), (92, 25), (92, 80)] + fg_test_points = points + [ + (points[0][0], points[0][1] - 1), + (points[0][0] + 1, points[0][1]), + (points[3][0] - 1, points[3][1]), + (points[3][0], points[3][1] - 1), + (points[2][0], points[2][1] + 1), + ] + bg_test_points = [ + (points[0][0] - 1, points[0][1]), + (points[0][0], points[0][1] + 1), + (points[0][0] - 1, points[0][1] + 1), + (points[0][0] + 1, points[0][1] - 1), + (points[3][0] + 1, points[3][1]), + (points[3][0], points[3][1] + 1), + (points[3][0] + 1, points[3][1] + 1), + (points[3][0] - 1, points[3][1] - 1), + (points[2][0] + 1, points[2][1]), + (points[2][0] - 1, points[2][1] + 1), + (points[1][0] - 1, points[1][1]), + (points[1][0], points[1][1] - 1), + (points[1][0] - 1, points[1][1] - 1), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.polygon(surf, points, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_aapolygon(self): + """aapolygon(surface, points, color): return None""" + fg = self.foreground_color + bg = self.background_color + points = [(10, 80), (10, 15), (92, 25), (92, 80)] + fg_test_points = points + bg_test_points = [ + (points[0][0] - 1, points[0][1]), + (points[0][0], points[0][1] + 1), + (points[0][0] - 1, points[0][1] + 1), + (points[0][0] + 1, points[0][1] - 1), + (points[3][0] + 1, points[3][1]), + (points[3][0], points[3][1] + 1), + (points[3][0] + 1, points[3][1] + 1), + (points[3][0] - 1, points[3][1] - 1), + (points[2][0] + 1, points[2][1]), + (points[2][0] - 1, points[2][1] + 1), + (points[1][0] - 1, points[1][1]), + (points[1][0], points[1][1] - 1), + (points[1][0] - 1, points[1][1] - 1), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.aapolygon(surf, points, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_not_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + @unittest.expectedFailure + def test_aapolygon__with_horizontal_edge(self): + """Ensure aapolygon draws horizontal edges correctly. + + This test creates 2 surfaces and draws a polygon on each. The pixels + on each surface are compared to ensure they are the same. The only + difference between the 2 polygons is that one is drawn using + aapolygon() and the other using multiple line() calls. They should + produce the same final drawing. + + Related to issue #622. + """ + bg_color = pygame.Color("white") + line_color = pygame.Color("black") + width, height = 11, 10 + expected_surface = pygame.Surface((width, height), 0, 32) + expected_surface.fill(bg_color) + surface = pygame.Surface((width, height), 0, 32) + surface.fill(bg_color) + + points = ((0, 0), (0, height - 1), (width - 1, height - 1), (width - 1, 0)) + + # The points are used to draw the expected aapolygon using the line() + # function. + for (x1, y1), (x2, y2) in zip(points, points[1:] + points[:1]): + pygame.gfxdraw.line(expected_surface, x1, y1, x2, y2, line_color) + + # The points in this order fail to draw the horizontal edge along + # the top. + pygame.gfxdraw.aapolygon(surface, points, line_color) + + # The surfaces are locked for a possible speed up of pixel access. + expected_surface.lock() + surface.lock() + for x in range(width): + for y in range(height): + self.assertEqual( + expected_surface.get_at((x, y)), + surface.get_at((x, y)), + "pos=({}, {})".format(x, y), + ) + + surface.unlock() + expected_surface.unlock() + + def test_filled_polygon(self): + """filled_polygon(surface, points, color): return None""" + fg = self.foreground_color + bg = self.background_color + points = [(10, 80), (10, 15), (92, 25), (92, 80)] + fg_test_points = points + [ + (points[0][0], points[0][1] - 1), + (points[0][0] + 1, points[0][1]), + (points[0][0] + 1, points[0][1] - 1), + (points[3][0] - 1, points[3][1]), + (points[3][0], points[3][1] - 1), + (points[3][0] - 1, points[3][1] - 1), + (points[2][0], points[2][1] + 1), + (points[2][0] - 1, points[2][1] + 1), + ] + bg_test_points = [ + (points[0][0] - 1, points[0][1]), + (points[0][0], points[0][1] + 1), + (points[0][0] - 1, points[0][1] + 1), + (points[3][0] + 1, points[3][1]), + (points[3][0], points[3][1] + 1), + (points[3][0] + 1, points[3][1] + 1), + (points[2][0] + 1, points[2][1]), + (points[1][0] - 1, points[1][1]), + (points[1][0], points[1][1] - 1), + (points[1][0] - 1, points[1][1] - 1), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.filled_polygon(surf, points, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_textured_polygon(self): + """textured_polygon(surface, points, texture, tx, ty): return None""" + w, h = self.default_size + fg = self.foreground_color + bg = self.background_color + tx = 0 + ty = 0 + texture = pygame.Surface((w + tx, h + ty), 0, 24) + texture.fill(fg, (0, 0, w, h)) + points = [(10, 80), (10, 15), (92, 25), (92, 80)] + # Don't know how to really check this as boarder points may + # or may not be included in the textured polygon. + fg_test_points = [(points[1][0] + 30, points[1][1] + 40)] + bg_test_points = [ + (points[0][0] - 1, points[0][1]), + (points[0][0], points[0][1] + 1), + (points[0][0] - 1, points[0][1] + 1), + (points[3][0] + 1, points[3][1]), + (points[3][0], points[3][1] + 1), + (points[3][0] + 1, points[3][1] + 1), + (points[2][0] + 1, points[2][1]), + (points[1][0] - 1, points[1][1]), + (points[1][0], points[1][1] - 1), + (points[1][0] - 1, points[1][1] - 1), + ] + for surf in self.surfaces[1:]: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.textured_polygon(surf, points, texture, -tx, -ty) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + # Alpha blit to 8 bits-per-pixel surface forbidden. + texture = pygame.Surface(self.default_size, SRCALPHA, 32) + self.assertRaises( + ValueError, + pygame.gfxdraw.textured_polygon, + self.surfaces[0], + points, + texture, + 0, + 0, + ) + + def test_bezier(self): + """bezier(surface, points, steps, color): return None""" + fg = self.foreground_color + bg = self.background_color + points = [(10, 50), (25, 15), (60, 80), (92, 30)] + fg_test_points = [points[0], points[3]] + bg_test_points = [ + (points[0][0] - 1, points[0][1]), + (points[3][0] + 1, points[3][1]), + (points[1][0], points[1][1] + 3), + (points[2][0], points[2][1] - 3), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.bezier(surf, points, 30, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/image__save_gl_surface_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/image__save_gl_surface_test.py new file mode 100644 index 0000000..2932f42 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/image__save_gl_surface_test.py @@ -0,0 +1,46 @@ +import os +import unittest + +from pygame.tests import test_utils +import pygame +from pygame.locals import * + + +@unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", + 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER', +) +class GL_ImageSave(unittest.TestCase): + def test_image_save_works_with_opengl_surfaces(self): + """ + |tags:display,slow,opengl| + """ + + pygame.display.init() + screen = pygame.display.set_mode((640, 480), OPENGL | DOUBLEBUF) + pygame.display.flip() + + tmp_dir = test_utils.get_tmp_dir() + # Try the imageext module. + tmp_file = os.path.join(tmp_dir, "opengl_save_surface_test.png") + pygame.image.save(screen, tmp_file) + + self.assertTrue(os.path.exists(tmp_file)) + + os.remove(tmp_file) + + # Only test the image module. + tmp_file = os.path.join(tmp_dir, "opengl_save_surface_test.bmp") + pygame.image.save(screen, tmp_file) + + self.assertTrue(os.path.exists(tmp_file)) + + os.remove(tmp_file) + + # stops tonnes of tmp dirs building up in trunk dir + os.rmdir(tmp_dir) + pygame.display.quit() + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/image_tags.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/image_tags.py new file mode 100644 index 0000000..d847903 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/image_tags.py @@ -0,0 +1,7 @@ +__tags__ = [] + +import pygame +import sys + +if "pygame.image" not in sys.modules: + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/image_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/image_test.py new file mode 100644 index 0000000..249fec9 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/image_test.py @@ -0,0 +1,586 @@ +# -*- coding: utf-8 -*- + +import array +import os +import tempfile +import unittest +import glob + +from pygame.tests.test_utils import example_path, png, tostring +import pygame, pygame.image, pygame.pkgdata +from pygame.compat import xrange_, ord_, unicode_ + + +def test_magic(f, magic_hex): + """ tests a given file to see if the magic hex matches. + """ + data = f.read(len(magic_hex)) + + if len(data) != len(magic_hex): + return 0 + + for i in range(len(magic_hex)): + if magic_hex[i] != ord_(data[i]): + return 0 + + return 1 + + +class ImageModuleTest(unittest.TestCase): + def testLoadIcon(self): + """ see if we can load the pygame icon. + """ + f = pygame.pkgdata.getResource("pygame_icon.bmp") + self.assertEqual(f.mode, "rb") + + surf = pygame.image.load_basic(f) + + self.assertEqual(surf.get_at((0, 0)), (5, 4, 5, 255)) + self.assertEqual(surf.get_height(), 32) + self.assertEqual(surf.get_width(), 32) + + def testLoadPNG(self): + """ see if we can load a png with color values in the proper channels. + """ + # Create a PNG file with known colors + reddish_pixel = (210, 0, 0, 255) + greenish_pixel = (0, 220, 0, 255) + bluish_pixel = (0, 0, 230, 255) + greyish_pixel = (110, 120, 130, 140) + pixel_array = [reddish_pixel + greenish_pixel, bluish_pixel + greyish_pixel] + + f_descriptor, f_path = tempfile.mkstemp(suffix=".png") + + with os.fdopen(f_descriptor, "wb") as f: + w = png.Writer(2, 2, alpha=True) + w.write(f, pixel_array) + + # Read the PNG file and verify that pygame interprets it correctly + surf = pygame.image.load(f_path) + + self.assertEqual(surf.get_at((0, 0)), reddish_pixel) + self.assertEqual(surf.get_at((1, 0)), greenish_pixel) + self.assertEqual(surf.get_at((0, 1)), bluish_pixel) + self.assertEqual(surf.get_at((1, 1)), greyish_pixel) + + # Read the PNG file obj. and verify that pygame interprets it correctly + with open(f_path, "rb") as f: + surf = pygame.image.load(f) + + self.assertEqual(surf.get_at((0, 0)), reddish_pixel) + self.assertEqual(surf.get_at((1, 0)), greenish_pixel) + self.assertEqual(surf.get_at((0, 1)), bluish_pixel) + self.assertEqual(surf.get_at((1, 1)), greyish_pixel) + + os.remove(f_path) + + def testLoadJPG(self): + """ see if we can load a jpg. + """ + + f = example_path("data/alien1.jpg") # normalized + # f = os.path.join("examples", "data", "alien1.jpg") + surf = pygame.image.load(f) + + with open(f, "rb") as f: + surf = pygame.image.load(f) + + # with open(os.path.join("examples", "data", "alien1.jpg"), "rb") as f: + # surf = pygame.image.load(open(os.path.join("examples", "data", + # "alien1.jpg"), "rb")) + + def testSaveJPG(self): + """ JPG equivalent to issue #211 - color channel swapping + + Make sure the SDL surface color masks represent the rgb memory format + required by the JPG library. The masks are machine endian dependent + """ + + from pygame import Color, Rect + + # The source image is a 2 by 2 square of four colors. Since JPEG is + # lossy, there can be color bleed. Make each color square 16 by 16, + # to avoid the significantly color value distorts found at color + # boundaries due to the compression value set by Pygame. + square_len = 16 + sz = 2 * square_len, 2 * square_len + + # +---------------------------------+ + # | red | green | + # |----------------+----------------| + # | blue | (255, 128, 64) | + # +---------------------------------+ + # + # as (rect, color) pairs. + def as_rect(square_x, square_y): + return Rect( + square_x * square_len, square_y * square_len, square_len, square_len + ) + + squares = [ + (as_rect(0, 0), Color("red")), + (as_rect(1, 0), Color("green")), + (as_rect(0, 1), Color("blue")), + (as_rect(1, 1), Color(255, 128, 64)), + ] + + # A surface format which is not directly usable with libjpeg. + surf = pygame.Surface(sz, 0, 32) + for rect, color in squares: + surf.fill(color, rect) + + # Assume pygame.image.Load works correctly as it is handled by the + # third party SDL_image library. + f_path = tempfile.mktemp(suffix=".jpg") + pygame.image.save(surf, f_path) + jpg_surf = pygame.image.load(f_path) + + # Allow for small differences in the restored colors. + def approx(c): + mask = 0xFC + return pygame.Color(c.r & mask, c.g & mask, c.b & mask) + + offset = square_len // 2 + for rect, color in squares: + posn = rect.move((offset, offset)).topleft + self.assertEqual(approx(jpg_surf.get_at(posn)), approx(color)) + + def testSavePNG32(self): + """ see if we can save a png with color values in the proper channels. + """ + # Create a PNG file with known colors + reddish_pixel = (215, 0, 0, 255) + greenish_pixel = (0, 225, 0, 255) + bluish_pixel = (0, 0, 235, 255) + greyish_pixel = (115, 125, 135, 145) + + surf = pygame.Surface((1, 4), pygame.SRCALPHA, 32) + surf.set_at((0, 0), reddish_pixel) + surf.set_at((0, 1), greenish_pixel) + surf.set_at((0, 2), bluish_pixel) + surf.set_at((0, 3), greyish_pixel) + + f_path = tempfile.mktemp(suffix=".png") + pygame.image.save(surf, f_path) + + try: + # Read the PNG file and verify that pygame saved it correctly + reader = png.Reader(filename=f_path) + width, height, pixels, metadata = reader.asRGBA8() + + # pixels is a generator + self.assertEqual(tuple(next(pixels)), reddish_pixel) + self.assertEqual(tuple(next(pixels)), greenish_pixel) + self.assertEqual(tuple(next(pixels)), bluish_pixel) + self.assertEqual(tuple(next(pixels)), greyish_pixel) + + finally: + # Ensures proper clean up. + if not reader.file.closed: + reader.file.close() + del reader + os.remove(f_path) + + def testSavePNG24(self): + """ see if we can save a png with color values in the proper channels. + """ + # Create a PNG file with known colors + reddish_pixel = (215, 0, 0) + greenish_pixel = (0, 225, 0) + bluish_pixel = (0, 0, 235) + greyish_pixel = (115, 125, 135) + + surf = pygame.Surface((1, 4), 0, 24) + surf.set_at((0, 0), reddish_pixel) + surf.set_at((0, 1), greenish_pixel) + surf.set_at((0, 2), bluish_pixel) + surf.set_at((0, 3), greyish_pixel) + + f_path = tempfile.mktemp(suffix=".png") + pygame.image.save(surf, f_path) + + try: + # Read the PNG file and verify that pygame saved it correctly + reader = png.Reader(filename=f_path) + width, height, pixels, metadata = reader.asRGB8() + + # pixels is a generator + self.assertEqual(tuple(next(pixels)), reddish_pixel) + self.assertEqual(tuple(next(pixels)), greenish_pixel) + self.assertEqual(tuple(next(pixels)), bluish_pixel) + self.assertEqual(tuple(next(pixels)), greyish_pixel) + + finally: + # Ensures proper clean up. + if not reader.file.closed: + reader.file.close() + del reader + os.remove(f_path) + + def test_save(self): + + s = pygame.Surface((10, 10)) + s.fill((23, 23, 23)) + magic_hex = {} + magic_hex["jpg"] = [0xFF, 0xD8, 0xFF, 0xE0] + magic_hex["png"] = [0x89, 0x50, 0x4E, 0x47] + # magic_hex['tga'] = [0x0, 0x0, 0xa] + magic_hex["bmp"] = [0x42, 0x4D] + + formats = ["jpg", "png", "bmp"] + # uppercase too... JPG + formats = formats + [x.upper() for x in formats] + + for fmt in formats: + try: + temp_filename = "%s.%s" % ("tmpimg", fmt) + pygame.image.save(s, temp_filename) + + # Using 'with' ensures the file is closed even if test fails. + with open(temp_filename, "rb") as handle: + # Test the magic numbers at the start of the file to ensure + # they are saved as the correct file type. + self.assertEqual( + (1, fmt), (test_magic(handle, magic_hex[fmt.lower()]), fmt) + ) + + # load the file to make sure it was saved correctly. + # Note load can load a jpg saved with a .png file name. + s2 = pygame.image.load(temp_filename) + # compare contents, might only work reliably for png... + # but because it's all one color it seems to work with jpg. + self.assertEqual(s2.get_at((0, 0)), s.get_at((0, 0))) + finally: + # clean up the temp file, comment out to leave tmp file after run. + os.remove(temp_filename) + + def test_save_colorkey(self): + """ make sure the color key is not changed when saving. + """ + s = pygame.Surface((10, 10), pygame.SRCALPHA, 32) + s.fill((23, 23, 23)) + s.set_colorkey((0, 0, 0)) + colorkey1 = s.get_colorkey() + p1 = s.get_at((0, 0)) + + temp_filename = "tmpimg.png" + try: + pygame.image.save(s, temp_filename) + s2 = pygame.image.load(temp_filename) + finally: + os.remove(temp_filename) + + colorkey2 = s.get_colorkey() + # check that the pixel and the colorkey is correct. + self.assertEqual(colorkey1, colorkey2) + self.assertEqual(p1, s2.get_at((0, 0))) + + def test_load_unicode_path(self): + import shutil + + orig = unicode_(example_path("data/asprite.bmp")) + temp = os.path.join(unicode_(example_path("data")), u"你好.bmp") + shutil.copy(orig, temp) + try: + im = pygame.image.load(temp) + finally: + os.remove(temp) + + def _unicode_save(self, temp_file): + im = pygame.Surface((10, 10), 0, 32) + try: + with open(temp_file, "w") as f: + pass + os.remove(temp_file) + except IOError: + raise unittest.SkipTest("the path cannot be opened") + + self.assertFalse(os.path.exists(temp_file)) + + try: + pygame.image.save(im, temp_file) + + self.assertGreater(os.path.getsize(temp_file), 10) + finally: + try: + os.remove(temp_file) + except EnvironmentError: + pass + + def test_save_unicode_path(self): + """save unicode object with non-ASCII chars""" + self._unicode_save(u"你好.bmp") + + def assertPremultipliedAreEqual(self, string1, string2, source_string): + self.assertEqual(len(string1), len(string2)) + block_size = 20 + if string1 != string2: + for block_start in xrange_(0, len(string1), block_size): + block_end = min(block_start + block_size, len(string1)) + block1 = string1[block_start:block_end] + block2 = string2[block_start:block_end] + if block1 != block2: + source_block = source_string[block_start:block_end] + msg = ( + "string difference in %d to %d of %d:\n%s\n%s\nsource:\n%s" + % ( + block_start, + block_end, + len(string1), + block1.encode("hex"), + block2.encode("hex"), + source_block.encode("hex"), + ) + ) + self.fail(msg) + + def test_to_string__premultiplied(self): + """ test to make sure we can export a surface to a premultiplied alpha string + """ + + def convertRGBAtoPremultiplied(surface_to_modify): + for x in xrange_(surface_to_modify.get_width()): + for y in xrange_(surface_to_modify.get_height()): + color = surface_to_modify.get_at((x, y)) + premult_color = ( + color[0] * color[3] / 255, + color[1] * color[3] / 255, + color[2] * color[3] / 255, + color[3], + ) + surface_to_modify.set_at((x, y), premult_color) + + test_surface = pygame.Surface((256, 256), pygame.SRCALPHA, 32) + for x in xrange_(test_surface.get_width()): + for y in xrange_(test_surface.get_height()): + i = x + y * test_surface.get_width() + test_surface.set_at( + (x, y), ((i * 7) % 256, (i * 13) % 256, (i * 27) % 256, y) + ) + premultiplied_copy = test_surface.copy() + convertRGBAtoPremultiplied(premultiplied_copy) + self.assertPremultipliedAreEqual( + pygame.image.tostring(test_surface, "RGBA_PREMULT"), + pygame.image.tostring(premultiplied_copy, "RGBA"), + pygame.image.tostring(test_surface, "RGBA"), + ) + self.assertPremultipliedAreEqual( + pygame.image.tostring(test_surface, "ARGB_PREMULT"), + pygame.image.tostring(premultiplied_copy, "ARGB"), + pygame.image.tostring(test_surface, "ARGB"), + ) + + no_alpha_surface = pygame.Surface((256, 256), 0, 24) + self.assertRaises( + ValueError, pygame.image.tostring, no_alpha_surface, "RGBA_PREMULT" + ) + + # Custom assert method to check for identical surfaces. + def _assertSurfaceEqual(self, surf_a, surf_b, msg=None): + a_width, a_height = surf_a.get_width(), surf_a.get_height() + + # Check a few things to see if the surfaces are equal. + self.assertEqual(a_width, surf_b.get_width(), msg) + self.assertEqual(a_height, surf_b.get_height(), msg) + self.assertEqual(surf_a.get_size(), surf_b.get_size(), msg) + self.assertEqual(surf_a.get_rect(), surf_b.get_rect(), msg) + self.assertEqual(surf_a.get_colorkey(), surf_b.get_colorkey(), msg) + self.assertEqual(surf_a.get_alpha(), surf_b.get_alpha(), msg) + self.assertEqual(surf_a.get_flags(), surf_b.get_flags(), msg) + self.assertEqual(surf_a.get_bitsize(), surf_b.get_bitsize(), msg) + self.assertEqual(surf_a.get_bytesize(), surf_b.get_bytesize(), msg) + # Anything else? + + # Making the method lookups local for a possible speed up. + surf_a_get_at = surf_a.get_at + surf_b_get_at = surf_b.get_at + for y in xrange_(a_height): + for x in xrange_(a_width): + self.assertEqual(surf_a_get_at((x, y)), surf_b_get_at((x, y)), msg) + + def test_fromstring__and_tostring(self): + """Ensure methods tostring() and fromstring() are symmetric.""" + + #################################################################### + def RotateRGBAtoARGB(str_buf): + byte_buf = array.array("B", str_buf) + num_quads = len(byte_buf) // 4 + for i in xrange_(num_quads): + alpha = byte_buf[i * 4 + 3] + byte_buf[i * 4 + 3] = byte_buf[i * 4 + 2] + byte_buf[i * 4 + 2] = byte_buf[i * 4 + 1] + byte_buf[i * 4 + 1] = byte_buf[i * 4 + 0] + byte_buf[i * 4 + 0] = alpha + return tostring(byte_buf) + + #################################################################### + def RotateARGBtoRGBA(str_buf): + byte_buf = array.array("B", str_buf) + num_quads = len(byte_buf) // 4 + for i in xrange_(num_quads): + alpha = byte_buf[i * 4 + 0] + byte_buf[i * 4 + 0] = byte_buf[i * 4 + 1] + byte_buf[i * 4 + 1] = byte_buf[i * 4 + 2] + byte_buf[i * 4 + 2] = byte_buf[i * 4 + 3] + byte_buf[i * 4 + 3] = alpha + return tostring(byte_buf) + + #################################################################### + test_surface = pygame.Surface((64, 256), flags=pygame.SRCALPHA, depth=32) + for i in xrange_(256): + for j in xrange_(16): + intensity = j * 16 + 15 + test_surface.set_at((j + 0, i), (intensity, i, i, i)) + test_surface.set_at((j + 16, i), (i, intensity, i, i)) + test_surface.set_at((j + 32, i), (i, i, intensity, i)) + test_surface.set_at((j + 32, i), (i, i, i, intensity)) + + self._assertSurfaceEqual( + test_surface, test_surface, "failing with identical surfaces" + ) + + rgba_buf = pygame.image.tostring(test_surface, "RGBA") + rgba_buf = RotateARGBtoRGBA(RotateRGBAtoARGB(rgba_buf)) + test_rotate_functions = pygame.image.fromstring( + rgba_buf, test_surface.get_size(), "RGBA" + ) + + self._assertSurfaceEqual( + test_surface, test_rotate_functions, "rotate functions are not symmetric" + ) + + rgba_buf = pygame.image.tostring(test_surface, "RGBA") + argb_buf = RotateRGBAtoARGB(rgba_buf) + test_from_argb_string = pygame.image.fromstring( + argb_buf, test_surface.get_size(), "ARGB" + ) + + self._assertSurfaceEqual( + test_surface, test_from_argb_string, '"RGBA" rotated to "ARGB" failed' + ) + + argb_buf = pygame.image.tostring(test_surface, "ARGB") + rgba_buf = RotateARGBtoRGBA(argb_buf) + test_to_argb_string = pygame.image.fromstring( + rgba_buf, test_surface.get_size(), "RGBA" + ) + + self._assertSurfaceEqual( + test_surface, test_to_argb_string, '"ARGB" rotated to "RGBA" failed' + ) + + for fmt in ("ARGB", "RGBA"): + fmt_buf = pygame.image.tostring(test_surface, fmt) + test_to_from_fmt_string = pygame.image.fromstring( + fmt_buf, test_surface.get_size(), fmt + ) + + self._assertSurfaceEqual( + test_surface, + test_to_from_fmt_string, + "tostring/fromstring functions are not " + 'symmetric with "{}" format'.format(fmt), + ) + + def test_tostring_depth_24(self): + test_surface = pygame.Surface((64, 256), depth=24) + for i in xrange_(256): + for j in xrange_(16): + intensity = j * 16 + 15 + test_surface.set_at((j + 0, i), (intensity, i, i, i)) + test_surface.set_at((j + 16, i), (i, intensity, i, i)) + test_surface.set_at((j + 32, i), (i, i, intensity, i)) + test_surface.set_at((j + 32, i), (i, i, i, intensity)) + + fmt = 'RGB' + fmt_buf = pygame.image.tostring(test_surface, fmt) + test_to_from_fmt_string = pygame.image.fromstring( + fmt_buf, test_surface.get_size(), fmt + ) + + self._assertSurfaceEqual( + test_surface, + test_to_from_fmt_string, + "tostring/fromstring functions are not " + 'symmetric with "{}" format'.format(fmt), + ) + + def todo_test_frombuffer(self): + + # __doc__ (as of 2008-08-02) for pygame.image.frombuffer: + + # pygame.image.frombuffer(string, size, format): return Surface + # create a new Surface that shares data inside a string buffer + # + # Create a new Surface that shares pixel data directly from the string + # buffer. This method takes the same arguments as + # pygame.image.fromstring(), but is unable to vertically flip the + # source data. + # + # This will run much faster than pygame.image.fromstring, since no + # pixel data must be allocated and copied. + + self.fail() + + def todo_test_get_extended(self): + + # __doc__ (as of 2008-08-02) for pygame.image.get_extended: + + # pygame.image.get_extended(): return bool + # test if extended image formats can be loaded + # + # If pygame is built with extended image formats this function will + # return True. It is still not possible to determine which formats + # will be available, but generally you will be able to load them all. + + self.fail() + + def todo_test_load_basic(self): + + # __doc__ (as of 2008-08-02) for pygame.image.load_basic: + + # pygame.image.load(filename): return Surface + # pygame.image.load(fileobj, namehint=): return Surface + # load new image from a file + + self.fail() + + def todo_test_load_extended(self): + + # __doc__ (as of 2008-08-02) for pygame.image.load_extended: + + # pygame module for image transfer + + self.fail() + + def todo_test_save_extended(self): + + # __doc__ (as of 2008-08-02) for pygame.image.save_extended: + + # pygame module for image transfer + + self.fail() + + def threads_load(self, images): + import pygame.threads + + for i in range(10): + surfs = pygame.threads.tmap(pygame.image.load, images) + for s in surfs: + self.assertIsInstance(s, pygame.Surface) + + def test_load_png_threads(self): + self.threads_load(glob.glob(example_path("data/*.png"))) + + def test_load_jpg_threads(self): + self.threads_load(glob.glob(example_path("data/*.jpg"))) + + def test_load_bmp_threads(self): + self.threads_load(glob.glob(example_path("data/*.bmp"))) + + def test_load_gif_threads(self): + self.threads_load(glob.glob(example_path("data/*.gif"))) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/imageext_tags.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/imageext_tags.py new file mode 100644 index 0000000..25cff74 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/imageext_tags.py @@ -0,0 +1,7 @@ +__tags__ = [] + +import pygame +import sys + +if "pygame.imageext" not in sys.modules: + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/imageext_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/imageext_test.py new file mode 100644 index 0000000..cd15b0a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/imageext_test.py @@ -0,0 +1,102 @@ +# -*- coding: utf8 -*- +import os +import os.path +import sys +import unittest + +from pygame.tests.test_utils import example_path +import pygame, pygame.image, pygame.pkgdata +from pygame.compat import as_unicode, unicode_ + +imageext = sys.modules["pygame.imageext"] + + +class ImageextModuleTest(unittest.TestCase): + # Most of the testing is done indirectly through image_test.py + # This just confirms file path encoding and error handling. + def test_save_non_string_file(self): + im = pygame.Surface((10, 10), 0, 32) + self.assertRaises(TypeError, imageext.save_extended, im, []) + + def test_load_non_string_file(self): + self.assertRaises(TypeError, imageext.load_extended, []) + + @unittest.skip("SDL silently removes invalid characters") + def test_save_bad_filename(self): + im = pygame.Surface((10, 10), 0, 32) + u = u"a\x00b\x00c.png" + self.assertRaises(pygame.error, imageext.save_extended, im, u) + + @unittest.skip("SDL silently removes invalid characters") + def test_load_bad_filename(self): + u = u"a\x00b\x00c.png" + self.assertRaises(pygame.error, imageext.load_extended, u) + + def test_save_unknown_extension(self): + im = pygame.Surface((10, 10), 0, 32) + s = "foo.bar" + self.assertRaises(pygame.error, imageext.save_extended, im, s) + if sys.version_info >= (3, 0): + def test_load_unknown_extension(self): + s = "foo.bar" + self.assertRaises(FileNotFoundError, imageext.load_extended, s) + + def test_load_unknown_file(self): + s = "nonexistent.png" + self.assertRaises(FileNotFoundError, imageext.load_extended, s) + else: + def test_load_unknown_extension(self): + s = "foo.bar" + self.assertRaises(IOError, imageext.load_extended, s) + + def test_load_unknown_file(self): + s = "nonexistent.png" + self.assertRaises(IOError, imageext.load_extended, s) + + def test_load_unicode_path_0(self): + u = unicode_(example_path("data/alien1.png")) + im = imageext.load_extended(u) + + def test_load_unicode_path_1(self): + """non-ASCII unicode""" + import shutil + + orig = unicode_(example_path("data/alien1.png")) + temp = os.path.join(unicode_(example_path("data")), u"你好.png") + shutil.copy(orig, temp) + try: + im = imageext.load_extended(temp) + finally: + os.remove(temp) + + def _unicode_save(self, temp_file): + im = pygame.Surface((10, 10), 0, 32) + try: + with open(temp_file, "w") as f: + pass + os.remove(temp_file) + except IOError: + raise unittest.SkipTest("the path cannot be opened") + + self.assertFalse(os.path.exists(temp_file)) + + try: + imageext.save_extended(im, temp_file) + + self.assertGreater(os.path.getsize(temp_file), 10) + finally: + try: + os.remove(temp_file) + except EnvironmentError: + pass + + def test_save_unicode_path_0(self): + """unicode object with ASCII chars""" + self._unicode_save(u"temp_file.png") + + def test_save_unicode_path_1(self): + self._unicode_save(u"你好.png") + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/joystick_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/joystick_test.py new file mode 100644 index 0000000..dd991df --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/joystick_test.py @@ -0,0 +1,94 @@ +import unittest + + +class JoystickTypeTest(unittest.TestCase): + def todo_test_Joystick(self): + + # __doc__ (as of 2008-08-02) for pygame.joystick.Joystick: + + # pygame.joystick.Joystick(id): return Joystick + # create a new Joystick object + # + # Create a new joystick to access a physical device. The id argument + # must be a value from 0 to pygame.joystick.get_count()-1. + # + # To access most of the Joystick methods, you'll need to init() the + # Joystick. This is separate from making sure the joystick module is + # initialized. When multiple Joysticks objects are created for the + # same physical joystick device (i.e., they have the same ID number), + # the state and values for those Joystick objects will be shared. + # + # The Joystick object allows you to get information about the types of + # controls on a joystick device. Once the device is initialized the + # Pygame event queue will start receiving events about its input. + # + # You can call the Joystick.get_name() and Joystick.get_id() functions + # without initializing the Joystick object. + # + + self.fail() + + +class JoytickModuleTest(unittest.TestCase): + def todo_test_get_count(self): + + # __doc__ (as of 2008-08-02) for pygame.joystick.get_count: + + # pygame.joystick.get_count(): return count + # number of joysticks on the system + # + # Return the number of joystick devices on the system. The count will + # be 0 if there are no joysticks on the system. + # + # When you create Joystick objects using Joystick(id), you pass an + # integer that must be lower than this count. + # + + self.fail() + + def todo_test_get_init(self): + + # __doc__ (as of 2008-08-02) for pygame.joystick.get_init: + + # pygame.joystick.get_init(): return bool + # true if the joystick module is initialized + # + # Test if the pygame.joystick.init() function has been called. + + self.fail() + + def todo_test_init(self): + + # __doc__ (as of 2008-08-02) for pygame.joystick.init: + + # pygame.joystick.init(): return None + # initialize the joystick module + # + # This function is called automatically by pygame.init(). + # It initializes the joystick module. This will scan the system for + # all joystick devices. The module must be initialized before any + # other functions will work. + # + # It is safe to call this function more than once. + + self.fail() + + def todo_test_quit(self): + + # __doc__ (as of 2008-08-02) for pygame.joystick.quit: + + # pygame.joystick.quit(): return None + # uninitialize the joystick module + # + # Uninitialize the joystick module. After you call this any existing + # joystick objects will no longer work. + # + # It is safe to call this function more than once. + + self.fail() + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/key_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/key_test.py new file mode 100644 index 0000000..76be093 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/key_test.py @@ -0,0 +1,81 @@ +import unittest +import pygame +import pygame.key + +SDL1 = pygame.get_sdl_version()[0] < 2 + + +class KeyModuleTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + pygame.init() + + @classmethod + def tearDownClass(cls): + pygame.quit() + + def setUp(cls): + # This makes sure pygame is always initialized before each test (in + # case a test calls pygame.quit()). + if not pygame.get_init(): + pygame.init() + + def test_import(self): + "does it import" + import pygame.key + + def todo_test_get_focused(self): + + # __doc__ (as of 2008-08-02) for pygame.key.get_focused: + + # pygame.key.get_focused(): return bool + # true if the display is receiving keyboard input from the system + # + # This is true when the display window has keyboard focus from the + # system. If the display needs to ensure it does not lose keyboard + # focus, it can use pygame.event.set_grab() to grab all input. + # + + self.fail() + + def test_get_pressed(self): + states = pygame.key.get_pressed() + self.assertEqual(states[pygame.K_RIGHT], 0) + + def test_name(self): + self.assertEqual(pygame.key.name(pygame.K_RETURN), "return") + self.assertEqual(pygame.key.name(pygame.K_0), "0") + self.assertEqual(pygame.key.name(pygame.K_SPACE), "space") + + def test_key_code(self): + if SDL1: + self.assertRaises(NotImplementedError, pygame.key.key_code, + "return") + else: + self.assertEqual(pygame.key.key_code("return"), pygame.K_RETURN) + self.assertEqual(pygame.key.key_code("0"), pygame.K_0) + self.assertEqual(pygame.key.key_code("space"), pygame.K_SPACE) + + self.assertRaises(ValueError, pygame.key.key_code, "fizzbuzz") + + def test_set_and_get_mods(self): + pygame.key.set_mods(pygame.KMOD_CTRL) + self.assertEqual(pygame.key.get_mods(), pygame.KMOD_CTRL) + + pygame.key.set_mods(pygame.KMOD_ALT) + self.assertEqual(pygame.key.get_mods(), pygame.KMOD_ALT) + pygame.key.set_mods(pygame.KMOD_CTRL | pygame.KMOD_ALT) + self.assertEqual(pygame.key.get_mods(), pygame.KMOD_CTRL | pygame.KMOD_ALT) + + def test_set_and_get_repeat(self): + self.assertEqual(pygame.key.get_repeat(), (0, 0)) + + pygame.key.set_repeat(10, 15) + self.assertEqual(pygame.key.get_repeat(), (10, 15)) + + pygame.key.set_repeat() + self.assertEqual(pygame.key.get_repeat(), (0, 0)) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/mask_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/mask_test.py new file mode 100644 index 0000000..240b045 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/mask_test.py @@ -0,0 +1,5934 @@ +from collections import OrderedDict +import copy +import random +import unittest +import sys + +import pygame +from pygame.locals import * +from pygame.math import Vector2 + + +def random_mask(size=(100, 100)): + """random_mask(size=(100,100)): return Mask + Create a mask of the given size, with roughly half the bits set at random.""" + m = pygame.Mask(size) + for i in range(size[0] * size[1] // 2): + x, y = random.randint(0, size[0] - 1), random.randint(0, size[1] - 1) + m.set_at((x, y)) + return m + + +def maskFromSurface(surface, threshold=127): + mask = pygame.Mask(surface.get_size()) + key = surface.get_colorkey() + if key: + for y in range(surface.get_height()): + for x in range(surface.get_width()): + if surface.get_at((x + 0.1, y + 0.1)) != key: + mask.set_at((x, y), 1) + else: + for y in range(surface.get_height()): + for x in range(surface.get_width()): + if surface.get_at((x, y))[3] > threshold: + mask.set_at((x, y), 1) + return mask + + +def create_bounding_rect(points): + """Creates a bounding rect from the given points.""" + xmin = xmax = points[0][0] + ymin = ymax = points[0][1] + + for x, y in points[1:]: + xmin = min(x, xmin) + xmax = max(x, xmax) + ymin = min(y, ymin) + ymax = max(y, ymax) + + return pygame.Rect((xmin, ymin), (xmax - xmin + 1, ymax - ymin + 1)) + + +def zero_size_pairs(width, height): + """Creates a generator which yields pairs of sizes. + + For each pair of sizes at least one of the sizes will have a 0 in it. + """ + sizes = ((width, height), (width, 0), (0, height), (0, 0)) + + return ((a, b) for a in sizes for b in sizes if 0 in a or 0 in b) + + +def corners(mask): + """Returns a tuple with the corner positions of the given mask. + + Clockwise from the top left corner. + """ + width, height = mask.get_size() + return ((0, 0), (width - 1, 0), (width - 1, height - 1), (0, height - 1)) + + +def assertSurfaceFilled(testcase, surface, expected_color, area_rect=None): + """Checks to see if the given surface is filled with the given color. + + If an area_rect is provided, only check that area of the surface. + """ + if area_rect is None: + x_range = range(surface.get_width()) + y_range = range(surface.get_height()) + else: + area_rect = area_rect.clip(surface.get_rect()) + x_range = range(area_rect.left, area_rect.right) + y_range = range(area_rect.top, area_rect.bottom) + + surface.lock() # Lock for possible speed up. + for pos in ((x, y) for y in y_range for x in x_range): + testcase.assertEqual(surface.get_at(pos), expected_color) + surface.unlock() + + +def assertSurfaceFilledIgnoreArea(testcase, surface, expected_color, ignore_rect): + """Checks if the surface is filled with the given color. The + ignore_rect area is not checked. + """ + x_range = range(surface.get_width()) + y_range = range(surface.get_height()) + + surface.lock() # Lock for possible speed up. + for pos in ((x, y) for y in y_range for x in x_range): + if not ignore_rect.collidepoint(pos): + testcase.assertEqual(surface.get_at(pos), expected_color) + surface.unlock() + + +def assertMaskEqual(testcase, m1, m2, msg=None): + """Checks to see if the 2 given masks are equal.""" + m1_count = m1.count() + + testcase.assertEqual(m1.get_size(), m2.get_size(), msg=msg) + testcase.assertEqual(m1_count, m2.count(), msg=msg) + testcase.assertEqual(m1_count, m1.overlap_area(m2, (0, 0)), msg=msg) + + # This can be used to help debug exact locations. + ##for i in range(m1.get_size()[0]): + ## for j in range(m1.get_size()[1]): + ## testcase.assertEqual(m1.get_at((i, j)), m2.get_at((i, j))) + + +class MaskTypeTest(unittest.TestCase): + ORIGIN_OFFSETS = ( + (0, 0), + (0, 1), + (1, 1), + (1, 0), + (1, -1), + (0, -1), + (-1, -1), + (-1, 0), + (-1, 1), + ) + + def test_mask(self): + """Ensure masks are created correctly without fill parameter.""" + expected_count = 0 + expected_size = (11, 23) + + mask1 = pygame.mask.Mask(expected_size) + mask2 = pygame.mask.Mask(size=expected_size) + + self.assertIsInstance(mask1, pygame.mask.Mask) + self.assertEqual(mask1.count(), expected_count) + self.assertEqual(mask1.get_size(), expected_size) + + self.assertIsInstance(mask2, pygame.mask.Mask) + self.assertEqual(mask2.count(), expected_count) + self.assertEqual(mask2.get_size(), expected_size) + + def test_mask__negative_size(self): + """Ensure the mask constructor handles negative sizes correctly.""" + for size in ((1, -1), (-1, 1), (-1, -1)): + with self.assertRaises(ValueError): + mask = pygame.Mask(size) + + def test_mask__fill_kwarg(self): + """Ensure masks are created correctly using the fill keyword.""" + width, height = 37, 47 + expected_size = (width, height) + fill_counts = {True: width * height, False: 0} + + for fill, expected_count in fill_counts.items(): + msg = "fill={}".format(fill) + + mask = pygame.mask.Mask(expected_size, fill=fill) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.count(), expected_count, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + + def test_mask__fill_kwarg_bit_boundaries(self): + """Ensures masks are created correctly using the fill keyword + over a range of sizes. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(1, 4): + for width in range(1, 66): + expected_count = width * height + expected_size = (width, height) + msg = "size={}".format(expected_size) + + mask = pygame.mask.Mask(expected_size, fill=True) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.count(), expected_count, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + + def test_mask__fill_arg(self): + """Ensure masks are created correctly using a fill arg.""" + width, height = 59, 71 + expected_size = (width, height) + fill_counts = {True: width * height, False: 0} + + for fill, expected_count in fill_counts.items(): + msg = "fill={}".format(fill) + + mask = pygame.mask.Mask(expected_size, fill) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.count(), expected_count, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + + def test_mask__size_kwarg(self): + """Ensure masks are created correctly using the size keyword.""" + width, height = 73, 83 + expected_size = (width, height) + fill_counts = {True: width * height, False: 0} + + for fill, expected_count in fill_counts.items(): + msg = "fill={}".format(fill) + + mask1 = pygame.mask.Mask(fill=fill, size=expected_size) + mask2 = pygame.mask.Mask(size=expected_size, fill=fill) + + self.assertIsInstance(mask1, pygame.mask.Mask, msg) + self.assertIsInstance(mask2, pygame.mask.Mask, msg) + self.assertEqual(mask1.count(), expected_count, msg) + self.assertEqual(mask2.count(), expected_count, msg) + self.assertEqual(mask1.get_size(), expected_size, msg) + self.assertEqual(mask2.get_size(), expected_size, msg) + + def test_copy(self): + """Ensures copy works correctly with some bits set and unset.""" + # Test different widths and heights. + for width in (31, 32, 33, 63, 64, 65): + for height in (31, 32, 33, 63, 64, 65): + mask = pygame.mask.Mask((width, height)) + + # Create a checkerboard pattern of set/unset bits. + for x in range(width): + for y in range(x & 1, height, 2): + mask.set_at((x, y)) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + + def test_copy__full(self): + """Ensures copy works correctly on a filled masked.""" + # Test different widths and heights. + for width in (31, 32, 33, 63, 64, 65): + for height in (31, 32, 33, 63, 64, 65): + mask = pygame.mask.Mask((width, height), fill=True) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + + def test_copy__empty(self): + """Ensures copy works correctly on an empty mask.""" + for width in (31, 32, 33, 63, 64, 65): + for height in (31, 32, 33, 63, 64, 65): + mask = pygame.mask.Mask((width, height)) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + + def test_copy__independent(self): + """Ensures copy makes an independent copy of the mask.""" + mask_set_pos = (64, 1) + mask_copy_set_pos = (64, 2) + mask = pygame.mask.Mask((65, 3)) + + # Test both the copy() and __copy__() methods. + mask_copies = (mask.copy(), copy.copy(mask)) + mask.set_at(mask_set_pos) + + for mask_copy in mask_copies: + mask_copy.set_at(mask_copy_set_pos) + + self.assertIsNot(mask_copy, mask) + self.assertNotEqual( + mask_copy.get_at(mask_set_pos), mask.get_at(mask_set_pos) + ) + self.assertNotEqual( + mask_copy.get_at(mask_copy_set_pos), mask.get_at(mask_copy_set_pos) + ) + + def test_get_size(self): + """Ensure a mask's size is correctly retrieved.""" + expected_size = (93, 101) + mask = pygame.mask.Mask(expected_size) + + self.assertEqual(mask.get_size(), expected_size) + + def test_get_rect(self): + """Ensures get_rect works correctly.""" + expected_rect = pygame.Rect((0, 0), (11, 13)) + + # Test on full and empty masks. + for fill in (True, False): + mask = pygame.mask.Mask(expected_rect.size, fill=fill) + + rect = mask.get_rect() + + self.assertEqual(rect, expected_rect) + + def test_get_rect__one_kwarg(self): + """Ensures get_rect supports a single rect attribute kwarg. + + Tests all the rect attributes. + """ + # Rect attributes that take a single value. + RECT_SINGLE_VALUE_ATTRIBUTES = ( + "x", + "y", + "top", + "left", + "bottom", + "right", + "centerx", + "centery", + "width", + "height", + "w", + "h", + ) + + # Rect attributes that take 2 values. + RECT_DOUBLE_VALUE_ATTRIBUTES = ( + "topleft", + "bottomleft", + "topright", + "bottomright", + "midtop", + "midleft", + "midbottom", + "midright", + "center", + "size", + ) + + # Testing ints/floats and tuples/lists/Vector2s. + # {attribute_names : attribute_values} + rect_attributes = { + RECT_SINGLE_VALUE_ATTRIBUTES: (3, 5.1), + RECT_DOUBLE_VALUE_ATTRIBUTES: ((1, 2.2), [2.3, 3], Vector2(0, 1)), + } + + size = (7, 3) + mask = pygame.mask.Mask(size) + + for attributes, values in rect_attributes.items(): + for attribute in attributes: + for value in values: + expected_rect = pygame.Rect((0, 0), size) + setattr(expected_rect, attribute, value) + + rect = mask.get_rect(**{attribute: value}) + + self.assertEqual(rect, expected_rect) + + def test_get_rect__multiple_kwargs(self): + """Ensures get_rect supports multiple rect attribute kwargs.""" + mask = pygame.mask.Mask((5, 4)) + expected_rect = pygame.Rect((0, 0), (0, 0)) + kwargs = {"x": 7.1, "top": -1, "size": Vector2(2, 3.2)} + + for attrib, value in kwargs.items(): + setattr(expected_rect, attrib, value) + + rect = mask.get_rect(**kwargs) + + self.assertEqual(rect, expected_rect) + + def test_get_rect__no_arg_support(self): + """Ensures get_rect only supports kwargs.""" + mask = pygame.mask.Mask((4, 5)) + + with self.assertRaises(TypeError): + rect = mask.get_rect(3) + + with self.assertRaises(TypeError): + rect = mask.get_rect((1, 2)) + + def test_get_rect__invalid_kwarg_name(self): + """Ensures get_rect detects invalid kwargs.""" + mask = pygame.mask.Mask((1, 2)) + + with self.assertRaises(AttributeError): + rect = mask.get_rect(righte=11) + + with self.assertRaises(AttributeError): + rect = mask.get_rect(toplef=(1, 1)) + + with self.assertRaises(AttributeError): + rect = mask.get_rect(move=(3, 2)) + + def test_get_rect__invalid_kwarg_format(self): + """Ensures get_rect detects invalid kwarg formats.""" + mask = pygame.mask.Mask((3, 11)) + + with self.assertRaises(TypeError): + rect = mask.get_rect(right="1") # Wrong type. + + with self.assertRaises(TypeError): + rect = mask.get_rect(bottom=(1,)) # Wrong type. + + with self.assertRaises(TypeError): + rect = mask.get_rect(centerx=(1, 1)) # Wrong type. + + with self.assertRaises(TypeError): + rect = mask.get_rect(midleft=(1, "1")) # Wrong type. + + with self.assertRaises(TypeError): + rect = mask.get_rect(topright=(1,)) # Too few. + + with self.assertRaises(TypeError): + rect = mask.get_rect(bottomleft=(1, 2, 3)) # Too many. + + with self.assertRaises(TypeError): + rect = mask.get_rect(midbottom=1) # Wrong type. + + def test_get_at(self): + """Ensure individual mask bits are correctly retrieved.""" + width, height = 5, 7 + mask0 = pygame.mask.Mask((width, height)) + mask1 = pygame.mask.Mask((width, height), fill=True) + mask0_expected_bit = 0 + mask1_expected_bit = 1 + pos = (width - 1, height - 1) + + # Check twice to make sure bits aren't toggled. + self.assertEqual(mask0.get_at(pos), mask0_expected_bit) + self.assertEqual(mask0.get_at(pos), mask0_expected_bit) + self.assertEqual(mask1.get_at(pos), mask1_expected_bit) + self.assertEqual(mask1.get_at(pos), mask1_expected_bit) + + def test_get_at__out_of_bounds(self): + """Ensure get_at() checks bounds.""" + width, height = 11, 3 + mask = pygame.mask.Mask((width, height)) + + with self.assertRaises(IndexError): + mask.get_at((width, 0)) + + with self.assertRaises(IndexError): + mask.get_at((0, height)) + + with self.assertRaises(IndexError): + mask.get_at((-1, 0)) + + with self.assertRaises(IndexError): + mask.get_at((0, -1)) + + def test_set_at(self): + """Ensure individual mask bits are set to 1.""" + width, height = 13, 17 + mask0 = pygame.mask.Mask((width, height)) + mask1 = pygame.mask.Mask((width, height), fill=True) + mask0_expected_count = 1 + mask1_expected_count = mask1.count() + expected_bit = 1 + pos = (width - 1, height - 1) + + mask0.set_at(pos, expected_bit) # set 0 to 1 + mask1.set_at(pos, expected_bit) # set 1 to 1 + + self.assertEqual(mask0.get_at(pos), expected_bit) + self.assertEqual(mask0.count(), mask0_expected_count) + self.assertEqual(mask1.get_at(pos), expected_bit) + self.assertEqual(mask1.count(), mask1_expected_count) + + def test_set_at__to_0(self): + """Ensure individual mask bits are set to 0.""" + width, height = 11, 7 + mask0 = pygame.mask.Mask((width, height)) + mask1 = pygame.mask.Mask((width, height), fill=True) + mask0_expected_count = 0 + mask1_expected_count = mask1.count() - 1 + expected_bit = 0 + pos = (width - 1, height - 1) + + mask0.set_at(pos, expected_bit) # set 0 to 0 + mask1.set_at(pos, expected_bit) # set 1 to 0 + + self.assertEqual(mask0.get_at(pos), expected_bit) + self.assertEqual(mask0.count(), mask0_expected_count) + self.assertEqual(mask1.get_at(pos), expected_bit) + self.assertEqual(mask1.count(), mask1_expected_count) + + def test_set_at__default_value(self): + """Ensure individual mask bits are set using the default value.""" + width, height = 3, 21 + mask0 = pygame.mask.Mask((width, height)) + mask1 = pygame.mask.Mask((width, height), fill=True) + mask0_expected_count = 1 + mask1_expected_count = mask1.count() + expected_bit = 1 + pos = (width - 1, height - 1) + + mask0.set_at(pos) # set 0 to 1 + mask1.set_at(pos) # set 1 to 1 + + self.assertEqual(mask0.get_at(pos), expected_bit) + self.assertEqual(mask0.count(), mask0_expected_count) + self.assertEqual(mask1.get_at(pos), expected_bit) + self.assertEqual(mask1.count(), mask1_expected_count) + + def test_set_at__out_of_bounds(self): + """Ensure set_at() checks bounds.""" + width, height = 11, 3 + mask = pygame.mask.Mask((width, height)) + + with self.assertRaises(IndexError): + mask.set_at((width, 0)) + + with self.assertRaises(IndexError): + mask.set_at((0, height)) + + with self.assertRaises(IndexError): + mask.set_at((-1, 0)) + + with self.assertRaises(IndexError): + mask.set_at((0, -1)) + + def test_overlap(self): + """Ensure the overlap intersection is correctly calculated. + + Testing the different combinations of full/empty masks: + (mask1-filled) 1 overlap 1 (mask2-filled) + (mask1-empty) 0 overlap 1 (mask2-filled) + (mask1-filled) 1 overlap 0 (mask2-empty) + (mask1-empty) 0 overlap 0 (mask2-empty) + """ + expected_size = (4, 4) + offset = (0, 0) + expected_default = None + expected_overlaps = {(True, True): offset} + + for fill2 in (True, False): + mask2 = pygame.mask.Mask(expected_size, fill=fill2) + mask2_count = mask2.count() + + for fill1 in (True, False): + key = (fill1, fill2) + msg = "key={}".format(key) + mask1 = pygame.mask.Mask(expected_size, fill=fill1) + mask1_count = mask1.count() + expected_pos = expected_overlaps.get(key, expected_default) + + overlap_pos = mask1.overlap(mask2, offset) + + self.assertEqual(overlap_pos, expected_pos, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), expected_size, msg) + self.assertEqual(mask2.get_size(), expected_size, msg) + + def test_overlap__offset(self): + """Ensure an offset overlap intersection is correctly calculated.""" + mask1 = pygame.mask.Mask((65, 3), fill=True) + mask2 = pygame.mask.Mask((66, 4), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + + for offset in self.ORIGIN_OFFSETS: + msg = "offset={}".format(offset) + expected_pos = (max(offset[0], 0), max(offset[1], 0)) + + overlap_pos = mask1.overlap(mask2, offset) + + self.assertEqual(overlap_pos, expected_pos, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + def test_overlap__offset_with_unset_bits(self): + """Ensure an offset overlap intersection is correctly calculated + when (0, 0) bits not set.""" + mask1 = pygame.mask.Mask((65, 3), fill=True) + mask2 = pygame.mask.Mask((66, 4), fill=True) + unset_pos = (0, 0) + mask1.set_at(unset_pos, 0) + mask2.set_at(unset_pos, 0) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + + for offset in self.ORIGIN_OFFSETS: + msg = "offset={}".format(offset) + x, y = offset + expected_y = max(y, 0) + if 0 == y: + expected_x = max(x + 1, 1) + elif 0 < y: + expected_x = max(x + 1, 0) + else: + expected_x = max(x, 1) + + overlap_pos = mask1.overlap(mask2, offset) + + self.assertEqual(overlap_pos, (expected_x, expected_y), msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + self.assertEqual(mask1.get_at(unset_pos), 0, msg) + self.assertEqual(mask2.get_at(unset_pos), 0, msg) + + def test_overlap__no_overlap(self): + """Ensure an offset overlap intersection is correctly calculated + when there is no overlap.""" + mask1 = pygame.mask.Mask((65, 3), fill=True) + mask1_count = mask1.count() + mask1_size = mask1.get_size() + + mask2_w, mask2_h = 67, 5 + mask2_size = (mask2_w, mask2_h) + mask2 = pygame.mask.Mask(mask2_size) + set_pos = (mask2_w - 1, mask2_h - 1) + mask2.set_at(set_pos) + mask2_count = 1 + + for offset in self.ORIGIN_OFFSETS: + msg = "offset={}".format(offset) + + overlap_pos = mask1.overlap(mask2, offset) + + self.assertIsNone(overlap_pos, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + self.assertEqual(mask2.get_at(set_pos), 1, msg) + + def test_overlap__offset_boundary(self): + """Ensures overlap handles offsets and boundaries correctly.""" + mask1 = pygame.mask.Mask((13, 3), fill=True) + mask2 = pygame.mask.Mask((7, 5), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + + # Check the 4 boundaries. + offsets = ( + (mask1_size[0], 0), # off right + (0, mask1_size[1]), # off bottom + (-mask2_size[0], 0), # off left + (0, -mask2_size[1]), + ) # off top + + for offset in offsets: + msg = "offset={}".format(offset) + + overlap_pos = mask1.overlap(mask2, offset) + + self.assertIsNone(overlap_pos, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + def test_overlap__bit_boundaries(self): + """Ensures overlap handles masks of different sizes correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(2, 4): + for width in range(2, 66): + mask_size = (width, height) + mask_count = width * height + mask1 = pygame.mask.Mask(mask_size, fill=True) + mask2 = pygame.mask.Mask(mask_size, fill=True) + + # Testing masks offset from each other. + for offset in self.ORIGIN_OFFSETS: + msg = "size={}, offset={}".format(mask_size, offset) + expected_pos = (max(offset[0], 0), max(offset[1], 0)) + + overlap_pos = mask1.overlap(mask2, offset) + + self.assertEqual(overlap_pos, expected_pos, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask_count, msg) + self.assertEqual(mask2.count(), mask_count, msg) + self.assertEqual(mask1.get_size(), mask_size, msg) + self.assertEqual(mask2.get_size(), mask_size, msg) + + def test_overlap__invalid_mask_arg(self): + """Ensure overlap handles invalid mask arguments correctly.""" + size = (5, 3) + offset = (0, 0) + mask = pygame.mask.Mask(size) + invalid_mask = pygame.Surface(size) + + with self.assertRaises(TypeError): + overlap_pos = mask.overlap(invalid_mask, offset) + + def test_overlap__invalid_offset_arg(self): + """Ensure overlap handles invalid offset arguments correctly.""" + size = (2, 7) + offset = "(0, 0)" + mask1 = pygame.mask.Mask(size) + mask2 = pygame.mask.Mask(size) + + with self.assertRaises(TypeError): + overlap_pos = mask1.overlap(mask2, offset) + + def test_overlap_area(self): + """Ensure the overlap_area is correctly calculated. + + Testing the different combinations of full/empty masks: + (mask1-filled) 1 overlap_area 1 (mask2-filled) + (mask1-empty) 0 overlap_area 1 (mask2-filled) + (mask1-filled) 1 overlap_area 0 (mask2-empty) + (mask1-empty) 0 overlap_area 0 (mask2-empty) + """ + expected_size = width, height = (4, 4) + offset = (0, 0) + expected_default = 0 + expected_counts = {(True, True): width * height} + + for fill2 in (True, False): + mask2 = pygame.mask.Mask(expected_size, fill=fill2) + mask2_count = mask2.count() + + for fill1 in (True, False): + key = (fill1, fill2) + msg = "key={}".format(key) + mask1 = pygame.mask.Mask(expected_size, fill=fill1) + mask1_count = mask1.count() + expected_count = expected_counts.get(key, expected_default) + + overlap_count = mask1.overlap_area(mask2, offset) + + self.assertEqual(overlap_count, expected_count, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), expected_size, msg) + self.assertEqual(mask2.get_size(), expected_size, msg) + + def test_overlap_area__offset(self): + """Ensure an offset overlap_area is correctly calculated.""" + mask1 = pygame.mask.Mask((65, 3), fill=True) + mask2 = pygame.mask.Mask((66, 4), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + for offset in self.ORIGIN_OFFSETS: + msg = "offset={}".format(offset) + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_count = overlap_rect.w * overlap_rect.h + + overlap_count = mask1.overlap_area(mask2, offset) + + self.assertEqual(overlap_count, expected_count, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + def test_overlap_area__offset_boundary(self): + """Ensures overlap_area handles offsets and boundaries correctly.""" + mask1 = pygame.mask.Mask((11, 3), fill=True) + mask2 = pygame.mask.Mask((5, 7), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + expected_count = 0 + + # Check the 4 boundaries. + offsets = ( + (mask1_size[0], 0), # off right + (0, mask1_size[1]), # off bottom + (-mask2_size[0], 0), # off left + (0, -mask2_size[1]), + ) # off top + + for offset in offsets: + msg = "offset={}".format(offset) + + overlap_count = mask1.overlap_area(mask2, offset) + + self.assertEqual(overlap_count, expected_count, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + def test_overlap_area__bit_boundaries(self): + """Ensures overlap_area handles masks of different sizes correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(2, 4): + for width in range(2, 66): + mask_size = (width, height) + mask_count = width * height + mask1 = pygame.mask.Mask(mask_size, fill=True) + mask2 = pygame.mask.Mask(mask_size, fill=True) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # Testing masks offset from each other. + for offset in self.ORIGIN_OFFSETS: + msg = "size={}, offset={}".format(mask_size, offset) + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_overlap_count = overlap_rect.w * overlap_rect.h + + overlap_count = mask1.overlap_area(mask2, offset) + + self.assertEqual(overlap_count, expected_overlap_count, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask_count, msg) + self.assertEqual(mask2.count(), mask_count, msg) + self.assertEqual(mask1.get_size(), mask_size, msg) + self.assertEqual(mask2.get_size(), mask_size, msg) + + def test_overlap_area__invalid_mask_arg(self): + """Ensure overlap_area handles invalid mask arguments correctly.""" + size = (3, 5) + offset = (0, 0) + mask = pygame.mask.Mask(size) + invalid_mask = pygame.Surface(size) + + with self.assertRaises(TypeError): + overlap_count = mask.overlap_area(invalid_mask, offset) + + def test_overlap_area__invalid_offset_arg(self): + """Ensure overlap_area handles invalid offset arguments correctly.""" + size = (7, 2) + offset = "(0, 0)" + mask1 = pygame.mask.Mask(size) + mask2 = pygame.mask.Mask(size) + + with self.assertRaises(TypeError): + overlap_count = mask1.overlap_area(mask2, offset) + + def test_overlap_mask(self): + """Ensure overlap_mask's mask has correct bits set. + + Testing the different combinations of full/empty masks: + (mask1-filled) 1 overlap_mask 1 (mask2-filled) + (mask1-empty) 0 overlap_mask 1 (mask2-filled) + (mask1-filled) 1 overlap_mask 0 (mask2-empty) + (mask1-empty) 0 overlap_mask 0 (mask2-empty) + """ + expected_size = (4, 4) + offset = (0, 0) + expected_default = pygame.mask.Mask(expected_size) + expected_masks = {(True, True): pygame.mask.Mask(expected_size, fill=True)} + + for fill2 in (True, False): + mask2 = pygame.mask.Mask(expected_size, fill=fill2) + mask2_count = mask2.count() + + for fill1 in (True, False): + key = (fill1, fill2) + msg = "key={}".format(key) + mask1 = pygame.mask.Mask(expected_size, fill=fill1) + mask1_count = mask1.count() + expected_mask = expected_masks.get(key, expected_default) + + overlap_mask = mask1.overlap_mask(mask2, offset) + + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) + assertMaskEqual(self, overlap_mask, expected_mask, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), expected_size, msg) + self.assertEqual(mask2.get_size(), expected_size, msg) + + def test_overlap_mask__bits_set(self): + """Ensure overlap_mask's mask has correct bits set.""" + mask1 = pygame.mask.Mask((50, 50), fill=True) + mask2 = pygame.mask.Mask((300, 10), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + + mask3 = mask1.overlap_mask(mask2, (-1, 0)) + + for i in range(50): + for j in range(10): + self.assertEqual(mask3.get_at((i, j)), 1, "({}, {})".format(i, j)) + + for i in range(50): + for j in range(11, 50): + self.assertEqual(mask3.get_at((i, j)), 0, "({}, {})".format(i, j)) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count) + self.assertEqual(mask2.count(), mask2_count) + self.assertEqual(mask1.get_size(), mask1_size) + self.assertEqual(mask2.get_size(), mask2_size) + + def test_overlap_mask__offset(self): + """Ensure an offset overlap_mask's mask is correctly calculated.""" + mask1 = pygame.mask.Mask((65, 3), fill=True) + mask2 = pygame.mask.Mask((66, 4), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + expected_mask = pygame.Mask(mask1_size) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + for offset in self.ORIGIN_OFFSETS: + msg = "offset={}".format(offset) + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_mask.clear() + expected_mask.draw( + pygame.Mask(overlap_rect.size, fill=True), overlap_rect.topleft + ) + + overlap_mask = mask1.overlap_mask(mask2, offset) + + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) + assertMaskEqual(self, overlap_mask, expected_mask, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + def test_overlap_mask__specific_offsets(self): + """Ensure an offset overlap_mask's mask is correctly calculated. + + Testing the specific case of: + -both masks are wider than 32 bits + -a positive offset is used + -the mask calling overlap_mask() is wider than the mask passed in + """ + mask1 = pygame.mask.Mask((65, 5), fill=True) + mask2 = pygame.mask.Mask((33, 3), fill=True) + expected_mask = pygame.Mask(mask1.get_size()) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # This rect's corners are used to move rect2 around the inside of + # rect1. + corner_rect = rect1.inflate(-2, -2) + + for corner in ("topleft", "topright", "bottomright", "bottomleft"): + setattr(rect2, corner, getattr(corner_rect, corner)) + offset = rect2.topleft + msg = "offset={}".format(offset) + overlap_rect = rect1.clip(rect2) + expected_mask.clear() + expected_mask.draw( + pygame.Mask(overlap_rect.size, fill=True), overlap_rect.topleft + ) + + overlap_mask = mask1.overlap_mask(mask2, offset) + + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) + assertMaskEqual(self, overlap_mask, expected_mask, msg) + + def test_overlap_mask__offset_boundary(self): + """Ensures overlap_mask handles offsets and boundaries correctly.""" + mask1 = pygame.mask.Mask((9, 3), fill=True) + mask2 = pygame.mask.Mask((11, 5), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + expected_count = 0 + expected_size = mask1_size + + # Check the 4 boundaries. + offsets = ( + (mask1_size[0], 0), # off right + (0, mask1_size[1]), # off bottom + (-mask2_size[0], 0), # off left + (0, -mask2_size[1]), + ) # off top + + for offset in offsets: + msg = "offset={}".format(offset) + + overlap_mask = mask1.overlap_mask(mask2, offset) + + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) + self.assertEqual(overlap_mask.count(), expected_count, msg) + self.assertEqual(overlap_mask.get_size(), expected_size, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + def test_overlap_mask__bit_boundaries(self): + """Ensures overlap_mask handles masks of different sizes correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(2, 4): + for width in range(2, 66): + mask_size = (width, height) + mask_count = width * height + mask1 = pygame.mask.Mask(mask_size, fill=True) + mask2 = pygame.mask.Mask(mask_size, fill=True) + expected_mask = pygame.Mask(mask_size) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # Testing masks offset from each other. + for offset in self.ORIGIN_OFFSETS: + msg = "size={}, offset={}".format(mask_size, offset) + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_mask.clear() + expected_mask.draw( + pygame.Mask(overlap_rect.size, fill=True), overlap_rect.topleft + ) + + overlap_mask = mask1.overlap_mask(mask2, offset) + + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) + assertMaskEqual(self, overlap_mask, expected_mask, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask_count, msg) + self.assertEqual(mask2.count(), mask_count, msg) + self.assertEqual(mask1.get_size(), mask_size, msg) + self.assertEqual(mask2.get_size(), mask_size, msg) + + def test_overlap_mask__invalid_mask_arg(self): + """Ensure overlap_mask handles invalid mask arguments correctly.""" + size = (3, 2) + offset = (0, 0) + mask = pygame.mask.Mask(size) + invalid_mask = pygame.Surface(size) + + with self.assertRaises(TypeError): + overlap_mask = mask.overlap_mask(invalid_mask, offset) + + def test_overlap_mask__invalid_offset_arg(self): + """Ensure overlap_mask handles invalid offset arguments correctly.""" + size = (5, 2) + offset = "(0, 0)" + mask1 = pygame.mask.Mask(size) + mask2 = pygame.mask.Mask(size) + + with self.assertRaises(TypeError): + overlap_mask = mask1.overlap_mask(mask2, offset) + + def test_mask_access(self): + """ do the set_at, and get_at parts work correctly? + """ + m = pygame.Mask((10, 10)) + m.set_at((0, 0), 1) + self.assertEqual(m.get_at((0, 0)), 1) + m.set_at((9, 0), 1) + self.assertEqual(m.get_at((9, 0)), 1) + + # s = pygame.Surface((10,10)) + # s.set_at((1,0), (0, 0, 1, 255)) + # self.assertEqual(s.get_at((1,0)), (0, 0, 1, 255)) + # s.set_at((-1,0), (0, 0, 1, 255)) + + # out of bounds, should get IndexError + self.assertRaises(IndexError, lambda: m.get_at((-1, 0))) + self.assertRaises(IndexError, lambda: m.set_at((-1, 0), 1)) + self.assertRaises(IndexError, lambda: m.set_at((10, 0), 1)) + self.assertRaises(IndexError, lambda: m.set_at((0, 10), 1)) + + def test_fill(self): + """Ensure a mask can be filled.""" + width, height = 11, 23 + expected_count = width * height + expected_size = (width, height) + mask = pygame.mask.Mask(expected_size) + + mask.fill() + + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + + def test_fill__bit_boundaries(self): + """Ensures masks of different sizes are filled correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(1, 4): + for width in range(1, 66): + mask = pygame.mask.Mask((width, height)) + expected_count = width * height + + mask.fill() + + self.assertEqual( + mask.count(), expected_count, "size=({}, {})".format(width, height) + ) + + def test_clear(self): + """Ensure a mask can be cleared.""" + expected_count = 0 + expected_size = (13, 27) + mask = pygame.mask.Mask(expected_size, fill=True) + + mask.clear() + + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + + def test_clear__bit_boundaries(self): + """Ensures masks of different sizes are cleared correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + expected_count = 0 + + for height in range(1, 4): + for width in range(1, 66): + mask = pygame.mask.Mask((width, height), fill=True) + + mask.clear() + + self.assertEqual( + mask.count(), expected_count, "size=({}, {})".format(width, height) + ) + + def test_invert(self): + """Ensure a mask can be inverted.""" + side = 73 + expected_size = (side, side) + mask1 = pygame.mask.Mask(expected_size) + mask2 = pygame.mask.Mask(expected_size, fill=True) + expected_count1 = side * side + expected_count2 = 0 + + for i in range(side): + expected_count1 -= 1 + expected_count2 += 1 + pos = (i, i) + mask1.set_at(pos) + mask2.set_at(pos, 0) + + mask1.invert() + mask2.invert() + + self.assertEqual(mask1.count(), expected_count1) + self.assertEqual(mask2.count(), expected_count2) + self.assertEqual(mask1.get_size(), expected_size) + self.assertEqual(mask2.get_size(), expected_size) + + for i in range(side): + pos = (i, i) + msg = "pos={}".format(pos) + + self.assertEqual(mask1.get_at(pos), 0, msg) + self.assertEqual(mask2.get_at(pos), 1, msg) + + def test_invert__full(self): + """Ensure a full mask can be inverted.""" + expected_count = 0 + expected_size = (43, 97) + mask = pygame.mask.Mask(expected_size, fill=True) + + mask.invert() + + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + + def test_invert__empty(self): + """Ensure an empty mask can be inverted.""" + width, height = 43, 97 + expected_size = (width, height) + expected_count = width * height + mask = pygame.mask.Mask(expected_size) + + mask.invert() + + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + + def test_invert__bit_boundaries(self): + """Ensures masks of different sizes are inverted correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for fill in (True, False): + for height in range(1, 4): + for width in range(1, 66): + mask = pygame.mask.Mask((width, height), fill=fill) + expected_count = 0 if fill else width * height + + mask.invert() + + self.assertEqual( + mask.count(), + expected_count, + "fill={}, size=({}, {})".format(fill, width, height), + ) + + def test_scale(self): + """Ensure a mask can be scaled.""" + width, height = 43, 61 + original_size = (width, height) + + for fill in (True, False): + original_mask = pygame.mask.Mask(original_size, fill=fill) + original_count = width * height if fill else 0 + + # Test a range of sizes. Also tests scaling to 'same' + # size when new_w, new_h = width, height + for new_w in range(width - 10, width + 10): + for new_h in range(height - 10, height + 10): + expected_size = (new_w, new_h) + expected_count = new_w * new_h if fill else 0 + msg = "size={}".format(expected_size) + + mask = original_mask.scale(expected_size) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.count(), expected_count, msg) + self.assertEqual(mask.get_size(), expected_size) + + # Ensure the original mask is unchanged. + self.assertEqual(original_mask.count(), original_count, msg) + self.assertEqual(original_mask.get_size(), original_size, msg) + + def test_scale__negative_size(self): + """Ensure scale handles negative sizes correctly.""" + mask = pygame.Mask((100, 100)) + + with self.assertRaises(ValueError): + mask.scale((-1, -1)) + + with self.assertRaises(ValueError): + mask.scale((-1, 10)) + + with self.assertRaises(ValueError): + mask.scale((10, -1)) + + def test_draw(self): + """Ensure a mask can be drawn onto another mask. + + Testing the different combinations of full/empty masks: + (mask1-filled) 1 draw 1 (mask2-filled) + (mask1-empty) 0 draw 1 (mask2-filled) + (mask1-filled) 1 draw 0 (mask2-empty) + (mask1-empty) 0 draw 0 (mask2-empty) + """ + expected_size = (4, 4) + offset = (0, 0) + expected_default = pygame.mask.Mask(expected_size, fill=True) + expected_masks = {(False, False): pygame.mask.Mask(expected_size)} + + for fill2 in (True, False): + mask2 = pygame.mask.Mask(expected_size, fill=fill2) + mask2_count = mask2.count() + + for fill1 in (True, False): + key = (fill1, fill2) + msg = "key={}".format(key) + mask1 = pygame.mask.Mask(expected_size, fill=fill1) + expected_mask = expected_masks.get(key, expected_default) + + mask1.draw(mask2, offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + + # Ensure mask2 unchanged. + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask2.get_size(), expected_size, msg) + + def test_draw__offset(self): + """Ensure an offset mask can be drawn onto another mask.""" + mask1 = pygame.mask.Mask((65, 3)) + mask2 = pygame.mask.Mask((66, 4), fill=True) + mask2_count = mask2.count() + mask2_size = mask2.get_size() + expected_mask = pygame.Mask(mask1.get_size()) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + for offset in self.ORIGIN_OFFSETS: + msg = "offset={}".format(offset) + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_mask.clear() + + # Normally draw() could be used to set these bits, but the draw() + # method is being tested here, so a loop is used instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y)) + mask1.clear() # Ensure it's empty for testing each offset. + + mask1.draw(mask2, offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + + # Ensure mask2 unchanged. + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + def test_draw__specific_offsets(self): + """Ensure an offset mask can be drawn onto another mask. + + Testing the specific case of: + -both masks are wider than 32 bits + -a positive offset is used + -the mask calling draw() is wider than the mask passed in + """ + mask1 = pygame.mask.Mask((65, 5)) + mask2 = pygame.mask.Mask((33, 3), fill=True) + expected_mask = pygame.Mask(mask1.get_size()) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # This rect's corners are used to move rect2 around the inside of + # rect1. + corner_rect = rect1.inflate(-2, -2) + + for corner in ("topleft", "topright", "bottomright", "bottomleft"): + setattr(rect2, corner, getattr(corner_rect, corner)) + offset = rect2.topleft + msg = "offset={}".format(offset) + overlap_rect = rect1.clip(rect2) + expected_mask.clear() + + # Normally draw() could be used to set these bits, but the draw() + # method is being tested here, so a loop is used instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y)) + mask1.clear() # Ensure it's empty for testing each offset. + + mask1.draw(mask2, offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + + def test_draw__offset_boundary(self): + """Ensures draw handles offsets and boundaries correctly.""" + mask1 = pygame.mask.Mask((13, 5)) + mask2 = pygame.mask.Mask((7, 3), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + + # Check the 4 boundaries. + offsets = ( + (mask1_size[0], 0), # off right + (0, mask1_size[1]), # off bottom + (-mask2_size[0], 0), # off left + (0, -mask2_size[1]), + ) # off top + + for offset in offsets: + msg = "offset={}".format(offset) + + mask1.draw(mask2, offset) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + def test_draw__bit_boundaries(self): + """Ensures draw handles masks of different sizes correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(2, 4): + for width in range(2, 66): + mask_size = (width, height) + mask_count = width * height + mask1 = pygame.mask.Mask(mask_size) + mask2 = pygame.mask.Mask(mask_size, fill=True) + expected_mask = pygame.Mask(mask_size) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # Testing masks offset from each other. + for offset in self.ORIGIN_OFFSETS: + msg = "size={}, offset={}".format(mask_size, offset) + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_mask.clear() + + # Normally draw() could be used to set these bits, but the + # draw() method is being tested here, so a loop is used + # instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y)) + mask1.clear() # Ensure it's empty for each test. + + mask1.draw(mask2, offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + + # Ensure mask2 unchanged. + self.assertEqual(mask2.count(), mask_count, msg) + self.assertEqual(mask2.get_size(), mask_size, msg) + + def test_draw__invalid_mask_arg(self): + """Ensure draw handles invalid mask arguments correctly.""" + size = (7, 3) + offset = (0, 0) + mask = pygame.mask.Mask(size) + invalid_mask = pygame.Surface(size) + + with self.assertRaises(TypeError): + mask.draw(invalid_mask, offset) + + def test_draw__invalid_offset_arg(self): + """Ensure draw handles invalid offset arguments correctly.""" + size = (5, 7) + offset = "(0, 0)" + mask1 = pygame.mask.Mask(size) + mask2 = pygame.mask.Mask(size) + + with self.assertRaises(TypeError): + mask1.draw(mask2, offset) + + def test_erase(self): + """Ensure a mask can erase another mask. + + Testing the different combinations of full/empty masks: + (mask1-filled) 1 erase 1 (mask2-filled) + (mask1-empty) 0 erase 1 (mask2-filled) + (mask1-filled) 1 erase 0 (mask2-empty) + (mask1-empty) 0 erase 0 (mask2-empty) + """ + expected_size = (4, 4) + offset = (0, 0) + expected_default = pygame.mask.Mask(expected_size) + expected_masks = {(True, False): pygame.mask.Mask(expected_size, fill=True)} + + for fill2 in (True, False): + mask2 = pygame.mask.Mask(expected_size, fill=fill2) + mask2_count = mask2.count() + + for fill1 in (True, False): + key = (fill1, fill2) + msg = "key={}".format(key) + mask1 = pygame.mask.Mask(expected_size, fill=fill1) + expected_mask = expected_masks.get(key, expected_default) + + mask1.erase(mask2, offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + + # Ensure mask2 unchanged. + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask2.get_size(), expected_size, msg) + + def test_erase__offset(self): + """Ensure an offset mask can erase another mask.""" + mask1 = pygame.mask.Mask((65, 3)) + mask2 = pygame.mask.Mask((66, 4), fill=True) + mask2_count = mask2.count() + mask2_size = mask2.get_size() + expected_mask = pygame.Mask(mask1.get_size()) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + for offset in self.ORIGIN_OFFSETS: + msg = "offset={}".format(offset) + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_mask.fill() + + # Normally erase() could be used to clear these bits, but the + # erase() method is being tested here, so a loop is used instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y), 0) + mask1.fill() # Ensure it's filled for testing each offset. + + mask1.erase(mask2, offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + + # Ensure mask2 unchanged. + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + def test_erase__specific_offsets(self): + """Ensure an offset mask can erase another mask. + + Testing the specific case of: + -both masks are wider than 32 bits + -a positive offset is used + -the mask calling erase() is wider than the mask passed in + """ + mask1 = pygame.mask.Mask((65, 5)) + mask2 = pygame.mask.Mask((33, 3), fill=True) + expected_mask = pygame.Mask(mask1.get_size()) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # This rect's corners are used to move rect2 around the inside of + # rect1. + corner_rect = rect1.inflate(-2, -2) + + for corner in ("topleft", "topright", "bottomright", "bottomleft"): + setattr(rect2, corner, getattr(corner_rect, corner)) + offset = rect2.topleft + msg = "offset={}".format(offset) + overlap_rect = rect1.clip(rect2) + expected_mask.fill() + + # Normally erase() could be used to clear these bits, but the + # erase() method is being tested here, so a loop is used instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y), 0) + mask1.fill() # Ensure it's filled for testing each offset. + + mask1.erase(mask2, offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + + def test_erase__offset_boundary(self): + """Ensures erase handles offsets and boundaries correctly.""" + mask1 = pygame.mask.Mask((7, 11), fill=True) + mask2 = pygame.mask.Mask((3, 13), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + + # Check the 4 boundaries. + offsets = ( + (mask1_size[0], 0), # off right + (0, mask1_size[1]), # off bottom + (-mask2_size[0], 0), # off left + (0, -mask2_size[1]), + ) # off top + + for offset in offsets: + msg = "offset={}".format(offset) + + mask1.erase(mask2, offset) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + def test_erase__bit_boundaries(self): + """Ensures erase handles masks of different sizes correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(2, 4): + for width in range(2, 66): + mask_size = (width, height) + mask_count = width * height + mask1 = pygame.mask.Mask(mask_size) + mask2 = pygame.mask.Mask(mask_size, fill=True) + expected_mask = pygame.Mask(mask_size) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # Testing masks offset from each other. + for offset in self.ORIGIN_OFFSETS: + msg = "size={}, offset={}".format(mask_size, offset) + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_mask.fill() + + # Normally erase() could be used to clear these bits, but + # the erase() method is being tested here, so a loop is + # used instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y), 0) + mask1.fill() # Ensure it's filled for each test. + + mask1.erase(mask2, offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + + # Ensure mask2 unchanged. + self.assertEqual(mask2.count(), mask_count, msg) + self.assertEqual(mask2.get_size(), mask_size, msg) + + def test_erase__invalid_mask_arg(self): + """Ensure erase handles invalid mask arguments correctly.""" + size = (3, 7) + offset = (0, 0) + mask = pygame.mask.Mask(size) + invalid_mask = pygame.Surface(size) + + with self.assertRaises(TypeError): + mask.erase(invalid_mask, offset) + + def test_erase__invalid_offset_arg(self): + """Ensure erase handles invalid offset arguments correctly.""" + size = (7, 5) + offset = "(0, 0)" + mask1 = pygame.mask.Mask(size) + mask2 = pygame.mask.Mask(size) + + with self.assertRaises(TypeError): + mask1.erase(mask2, offset) + + def test_count(self): + """Ensure a mask's set bits are correctly counted.""" + side = 67 + expected_size = (side, side) + expected_count = 0 + mask = pygame.mask.Mask(expected_size) + + for i in range(side): + expected_count += 1 + mask.set_at((i, i)) + + count = mask.count() + + self.assertEqual(count, expected_count) + self.assertEqual(mask.get_size(), expected_size) + + def test_count__bit_boundaries(self): + """Ensures the set bits of different sized masks are counted correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for fill in (True, False): + for height in range(1, 4): + for width in range(1, 66): + mask = pygame.mask.Mask((width, height), fill=fill) + expected_count = width * height if fill else 0 + + # Test toggling each bit. + for pos in ((x, y) for y in range(height) for x in range(width)): + if fill: + mask.set_at(pos, 0) + expected_count -= 1 + else: + mask.set_at(pos, 1) + expected_count += 1 + + count = mask.count() + + self.assertEqual( + count, + expected_count, + "fill={}, size=({}, {}), pos={}".format( + fill, width, height, pos + ), + ) + + def test_count__full_mask(self): + """Ensure a full mask's set bits are correctly counted.""" + width, height = 17, 97 + expected_size = (width, height) + expected_count = width * height + mask = pygame.mask.Mask(expected_size, fill=True) + + count = mask.count() + + self.assertEqual(count, expected_count) + self.assertEqual(mask.get_size(), expected_size) + + def test_count__empty_mask(self): + """Ensure an empty mask's set bits are correctly counted.""" + expected_count = 0 + expected_size = (13, 27) + mask = pygame.mask.Mask(expected_size) + + count = mask.count() + + self.assertEqual(count, expected_count) + self.assertEqual(mask.get_size(), expected_size) + + def test_centroid(self): + """Ensure a filled mask's centroid is correctly calculated.""" + mask = pygame.mask.Mask((5, 7), fill=True) + expected_centroid = mask.get_rect().center + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_centroid__empty_mask(self): + """Ensure an empty mask's centroid is correctly calculated.""" + expected_centroid = (0, 0) + expected_size = (101, 103) + mask = pygame.mask.Mask(expected_size) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + self.assertEqual(mask.get_size(), expected_size) + + def test_centroid__single_row(self): + """Ensure a mask's centroid is correctly calculated + when setting points along a single row.""" + width, height = (5, 7) + mask = pygame.mask.Mask((width, height)) + + for y in range(height): + mask.clear() # Clear for each row. + + for x in range(width): + mask.set_at((x, y)) + expected_centroid = (x // 2, y) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_centroid__two_rows(self): + """Ensure a mask's centroid is correctly calculated + when setting points along two rows.""" + width, height = (5, 7) + mask = pygame.mask.Mask((width, height)) + + # The first row is tested with each of the other rows. + for y in range(1, height): + mask.clear() # Clear for each set of rows. + + for x in range(width): + mask.set_at((x, 0)) + mask.set_at((x, y)) + expected_centroid = (x // 2, y // 2) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_centroid__single_column(self): + """Ensure a mask's centroid is correctly calculated + when setting points along a single column.""" + width, height = (5, 7) + mask = pygame.mask.Mask((width, height)) + + for x in range(width): + mask.clear() # Clear for each column. + + for y in range(height): + mask.set_at((x, y)) + expected_centroid = (x, y // 2) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_centroid__two_columns(self): + """Ensure a mask's centroid is correctly calculated + when setting points along two columns.""" + width, height = (5, 7) + mask = pygame.mask.Mask((width, height)) + + # The first column is tested with each of the other columns. + for x in range(1, width): + mask.clear() # Clear for each set of columns. + + for y in range(height): + mask.set_at((0, y)) + mask.set_at((x, y)) + expected_centroid = (x // 2, y // 2) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_centroid__all_corners(self): + """Ensure a mask's centroid is correctly calculated + when its corners are set.""" + mask = pygame.mask.Mask((5, 7)) + expected_centroid = mask.get_rect().center + + for corner in corners(mask): + mask.set_at(corner) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_centroid__two_corners(self): + """Ensure a mask's centroid is correctly calculated + when only two corners are set.""" + mask = pygame.mask.Mask((5, 7)) + mask_rect = mask.get_rect() + mask_corners = corners(mask) + + for i, corner1 in enumerate(mask_corners): + for corner2 in mask_corners[i + 1 :]: + mask.clear() # Clear for each pair of corners. + mask.set_at(corner1) + mask.set_at(corner2) + + if corner1[0] == corner2[0]: + expected_centroid = (corner1[0], abs(corner1[1] - corner2[1]) // 2) + elif corner1[1] == corner2[1]: + expected_centroid = (abs(corner1[0] - corner2[0]) // 2, corner1[1]) + else: + expected_centroid = mask_rect.center + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def todo_test_angle(self): + """Ensure a mask's orientation angle is correctly calculated.""" + self.fail() + + def test_angle__empty_mask(self): + """Ensure an empty mask's angle is correctly calculated.""" + expected_angle = 0.0 + expected_size = (107, 43) + mask = pygame.mask.Mask(expected_size) + + angle = mask.angle() + + self.assertIsInstance(angle, float) + self.assertAlmostEqual(angle, expected_angle) + self.assertEqual(mask.get_size(), expected_size) + + def test_drawing(self): + """ Test fill, clear, invert, draw, erase + """ + m = pygame.Mask((100, 100)) + self.assertEqual(m.count(), 0) + + m.fill() + self.assertEqual(m.count(), 10000) + + m2 = pygame.Mask((10, 10), fill=True) + m.erase(m2, (50, 50)) + self.assertEqual(m.count(), 9900) + + m.invert() + self.assertEqual(m.count(), 100) + + m.draw(m2, (0, 0)) + self.assertEqual(m.count(), 200) + + m.clear() + self.assertEqual(m.count(), 0) + + def test_outline(self): + """ + """ + + m = pygame.Mask((20, 20)) + self.assertEqual(m.outline(), []) + + m.set_at((10, 10), 1) + self.assertEqual(m.outline(), [(10, 10)]) + + m.set_at((10, 12), 1) + self.assertEqual(m.outline(10), [(10, 10)]) + + m.set_at((11, 11), 1) + self.assertEqual( + m.outline(), [(10, 10), (11, 11), (10, 12), (11, 11), (10, 10)] + ) + self.assertEqual(m.outline(2), [(10, 10), (10, 12), (10, 10)]) + + # TODO: Test more corner case outlines. + + def test_convolve__size(self): + sizes = [(1, 1), (31, 31), (32, 32), (100, 100)] + for s1 in sizes: + m1 = pygame.Mask(s1) + for s2 in sizes: + m2 = pygame.Mask(s2) + o = m1.convolve(m2) + + self.assertIsInstance(o, pygame.mask.Mask) + + for i in (0, 1): + self.assertEqual( + o.get_size()[i], m1.get_size()[i] + m2.get_size()[i] - 1 + ) + + def test_convolve__point_identities(self): + """Convolving with a single point is the identity, while convolving a point with something flips it.""" + m = random_mask((100, 100)) + k = pygame.Mask((1, 1)) + k.set_at((0, 0)) + + convolve_mask = m.convolve(k) + + self.assertIsInstance(convolve_mask, pygame.mask.Mask) + assertMaskEqual(self, m, convolve_mask) + + convolve_mask = k.convolve(k.convolve(m)) + + self.assertIsInstance(convolve_mask, pygame.mask.Mask) + assertMaskEqual(self, m, convolve_mask) + + def test_convolve__with_output(self): + """checks that convolution modifies only the correct portion of the output""" + + m = random_mask((10, 10)) + k = pygame.Mask((2, 2)) + k.set_at((0, 0)) + + o = pygame.Mask((50, 50)) + test = pygame.Mask((50, 50)) + + m.convolve(k, o) + test.draw(m, (1, 1)) + + self.assertIsInstance(o, pygame.mask.Mask) + assertMaskEqual(self, o, test) + + o.clear() + test.clear() + + m.convolve(k, o, (10, 10)) + test.draw(m, (11, 11)) + + self.assertIsInstance(o, pygame.mask.Mask) + assertMaskEqual(self, o, test) + + def test_convolve__out_of_range(self): + full = pygame.Mask((2, 2), fill=True) + # Tuple of points (out of range) and the expected count for each. + pts_data = (((0, 3), 0), ((0, 2), 3), ((-2, -2), 1), ((-3, -3), 0)) + + for pt, expected_count in pts_data: + convolve_mask = full.convolve(full, None, pt) + + self.assertIsInstance(convolve_mask, pygame.mask.Mask) + self.assertEqual(convolve_mask.count(), expected_count) + + def test_convolve(self): + """Tests the definition of convolution""" + m1 = random_mask((100, 100)) + m2 = random_mask((100, 100)) + conv = m1.convolve(m2) + + self.assertIsInstance(conv, pygame.mask.Mask) + for i in range(conv.get_size()[0]): + for j in range(conv.get_size()[1]): + self.assertEqual( + conv.get_at((i, j)) == 0, m1.overlap(m2, (i - 99, j - 99)) is None + ) + + def _draw_component_pattern_box(self, mask, size, pos, inverse=False): + # Helper method to create/draw a 'box' pattern for testing. + # + # 111 + # 101 3x3 example pattern + # 111 + pattern = pygame.mask.Mask((size, size), fill=True) + pattern.set_at((size // 2, size // 2), 0) + + if inverse: + mask.erase(pattern, pos) + pattern.invert() + else: + mask.draw(pattern, pos) + + return pattern + + def _draw_component_pattern_x(self, mask, size, pos, inverse=False): + # Helper method to create/draw an 'X' pattern for testing. + # + # 101 + # 010 3x3 example pattern + # 101 + pattern = pygame.mask.Mask((size, size)) + + ymax = size - 1 + for y in range(size): + for x in range(size): + if x == y or x == ymax - y: + pattern.set_at((x, y)) + + if inverse: + mask.erase(pattern, pos) + pattern.invert() + else: + mask.draw(pattern, pos) + + return pattern + + def _draw_component_pattern_plus(self, mask, size, pos, inverse=False): + # Helper method to create/draw a '+' pattern for testing. + # + # 010 + # 111 3x3 example pattern + # 010 + pattern = pygame.mask.Mask((size, size)) + + xmid = ymid = size // 2 + for y in range(size): + for x in range(size): + if x == xmid or y == ymid: + pattern.set_at((x, y)) + + if inverse: + mask.erase(pattern, pos) + pattern.invert() + else: + mask.draw(pattern, pos) + + return pattern + + def test_connected_component(self): + """Ensure a mask's connected component is correctly calculated.""" + width, height = 41, 27 + expected_size = (width, height) + original_mask = pygame.mask.Mask(expected_size) + patterns = [] # Patterns and offsets. + + # Draw some connected patterns on the original mask. + offset = (0, 0) + pattern = self._draw_component_pattern_x(original_mask, 3, offset) + patterns.append((pattern, offset)) + + size = 4 + offset = (width - size, 0) + pattern = self._draw_component_pattern_plus(original_mask, size, offset) + patterns.append((pattern, offset)) + + # Make this one the largest connected component. + offset = (width // 2, height // 2) + pattern = self._draw_component_pattern_box(original_mask, 7, offset) + patterns.append((pattern, offset)) + + expected_pattern, expected_offset = patterns[-1] + expected_count = expected_pattern.count() + original_count = sum(p.count() for p, _ in patterns) + + mask = original_mask.connected_component() + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + self.assertEqual( + mask.overlap_area(expected_pattern, expected_offset), expected_count + ) + + # Ensure the original mask is unchanged. + self.assertEqual(original_mask.count(), original_count) + self.assertEqual(original_mask.get_size(), expected_size) + + for pattern, offset in patterns: + self.assertEqual( + original_mask.overlap_area(pattern, offset), pattern.count() + ) + + def test_connected_component__full_mask(self): + """Ensure a mask's connected component is correctly calculated + when the mask is full. + """ + expected_size = (23, 31) + original_mask = pygame.mask.Mask(expected_size, fill=True) + expected_count = original_mask.count() + + mask = original_mask.connected_component() + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + + # Ensure the original mask is unchanged. + self.assertEqual(original_mask.count(), expected_count) + self.assertEqual(original_mask.get_size(), expected_size) + + def test_connected_component__empty_mask(self): + """Ensure a mask's connected component is correctly calculated + when the mask is empty. + """ + expected_size = (37, 43) + original_mask = pygame.mask.Mask(expected_size) + original_count = original_mask.count() + expected_count = 0 + + mask = original_mask.connected_component() + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + + # Ensure the original mask is unchanged. + self.assertEqual(original_mask.count(), original_count) + self.assertEqual(original_mask.get_size(), expected_size) + + def test_connected_component__one_set_bit(self): + """Ensure a mask's connected component is correctly calculated + when the coordinate's bit is set with a connected component of 1 bit. + """ + width, height = 71, 67 + expected_size = (width, height) + original_mask = pygame.mask.Mask(expected_size, fill=True) + xset, yset = width // 2, height // 2 + set_pos = (xset, yset) + expected_offset = (xset - 1, yset - 1) + + # This isolates the bit at set_pos from all the other bits. + expected_pattern = self._draw_component_pattern_box( + original_mask, 3, expected_offset, inverse=True + ) + expected_count = 1 + original_count = original_mask.count() + + mask = original_mask.connected_component(set_pos) + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + self.assertEqual( + mask.overlap_area(expected_pattern, expected_offset), expected_count + ) + + # Ensure the original mask is unchanged. + self.assertEqual(original_mask.count(), original_count) + self.assertEqual(original_mask.get_size(), expected_size) + self.assertEqual( + original_mask.overlap_area(expected_pattern, expected_offset), + expected_count, + ) + + def test_connected_component__multi_set_bits(self): + """Ensure a mask's connected component is correctly calculated + when the coordinate's bit is set with a connected component of > 1 bit. + """ + expected_size = (113, 67) + original_mask = pygame.mask.Mask(expected_size) + p_width, p_height = 11, 13 + set_pos = xset, yset = 11, 21 + expected_offset = (xset - 1, yset - 1) + expected_pattern = pygame.mask.Mask((p_width, p_height), fill=True) + + # Make an unsymmetrical pattern. All the set bits need to be connected + # in the resulting pattern for this to work properly. + for y in range(3, p_height): + for x in range(1, p_width): + if x == y or x == y - 3 or x == p_width - 4: + expected_pattern.set_at((x, y), 0) + + expected_count = expected_pattern.count() + original_mask.draw(expected_pattern, expected_offset) + + mask = original_mask.connected_component(set_pos) + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + self.assertEqual( + mask.overlap_area(expected_pattern, expected_offset), expected_count + ) + + # Ensure the original mask is unchanged. + self.assertEqual(original_mask.count(), expected_count) + self.assertEqual(original_mask.get_size(), expected_size) + self.assertEqual( + original_mask.overlap_area(expected_pattern, expected_offset), + expected_count, + ) + + def test_connected_component__unset_bit(self): + """Ensure a mask's connected component is correctly calculated + when the coordinate's bit is unset. + """ + width, height = 109, 101 + expected_size = (width, height) + original_mask = pygame.mask.Mask(expected_size, fill=True) + unset_pos = (width // 2, height // 2) + original_mask.set_at(unset_pos, 0) + original_count = original_mask.count() + expected_count = 0 + + mask = original_mask.connected_component(unset_pos) + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + + # Ensure the original mask is unchanged. + self.assertEqual(original_mask.count(), original_count) + self.assertEqual(original_mask.get_size(), expected_size) + self.assertEqual(original_mask.get_at(unset_pos), 0) + + def test_connected_component__out_of_bounds(self): + """Ensure connected_component() checks bounds.""" + width, height = 19, 11 + original_size = (width, height) + original_mask = pygame.mask.Mask(original_size, fill=True) + original_count = original_mask.count() + + for pos in ((0, -1), (-1, 0), (0, height + 1), (width + 1, 0)): + with self.assertRaises(IndexError): + mask = original_mask.connected_component(pos) + + # Ensure the original mask is unchanged. + self.assertEqual(original_mask.count(), original_count) + self.assertEqual(original_mask.get_size(), original_size) + + def test_connected_components(self): + """ + """ + m = pygame.Mask((10, 10)) + + self.assertListEqual(m.connected_components(), []) + + comp = m.connected_component() + + self.assertEqual(m.count(), comp.count()) + + m.set_at((0, 0), 1) + m.set_at((1, 1), 1) + comp = m.connected_component() + comps = m.connected_components() + comps1 = m.connected_components(1) + comps2 = m.connected_components(2) + comps3 = m.connected_components(3) + + self.assertEqual(comp.count(), comps[0].count()) + self.assertEqual(comps1[0].count(), 2) + self.assertEqual(comps2[0].count(), 2) + self.assertListEqual(comps3, []) + + m.set_at((9, 9), 1) + comp = m.connected_component() + comp1 = m.connected_component((1, 1)) + comp2 = m.connected_component((2, 2)) + comps = m.connected_components() + comps1 = m.connected_components(1) + comps2 = m.connected_components(2) + comps3 = m.connected_components(3) + + self.assertEqual(comp.count(), 2) + self.assertEqual(comp1.count(), 2) + self.assertEqual(comp2.count(), 0) + self.assertEqual(len(comps), 2) + self.assertEqual(len(comps1), 2) + self.assertEqual(len(comps2), 1) + self.assertEqual(len(comps3), 0) + + for mask in comps: + self.assertIsInstance(mask, pygame.mask.Mask) + + def test_connected_components__negative_min_with_empty_mask(self): + """Ensures connected_components() properly handles negative min values + when the mask is empty. + + Negative and zero values for the min parameter (minimum number of bits + per connected component) equate to setting it to one. + """ + expected_comps = [] + mask_count = 0 + mask_size = (65, 13) + mask = pygame.mask.Mask(mask_size) + + connected_comps = mask.connected_components(-1) + + self.assertListEqual(connected_comps, expected_comps) + + # Ensure the original mask is unchanged. + self.assertEqual(mask.count(), mask_count) + self.assertEqual(mask.get_size(), mask_size) + + def test_connected_components__negative_min_with_full_mask(self): + """Ensures connected_components() properly handles negative min values + when the mask is full. + + Negative and zero values for the min parameter (minimum number of bits + per connected component) equate to setting it to one. + """ + mask_size = (64, 11) + mask = pygame.mask.Mask(mask_size, fill=True) + mask_count = mask.count() + expected_len = 1 + + connected_comps = mask.connected_components(-2) + + self.assertEqual(len(connected_comps), expected_len) + assertMaskEqual(self, connected_comps[0], mask) + + # Ensure the original mask is unchanged. + self.assertEqual(mask.count(), mask_count) + self.assertEqual(mask.get_size(), mask_size) + + def test_connected_components__negative_min_with_some_bits_set(self): + """Ensures connected_components() properly handles negative min values + when the mask has some bits set. + + Negative and zero values for the min parameter (minimum number of bits + per connected component) equate to setting it to one. + """ + mask_size = (64, 12) + mask = pygame.mask.Mask(mask_size) + expected_comps = {} + + # Set the corners and the center positions. A new expected component + # mask is created for each point. + for corner in corners(mask): + mask.set_at(corner) + + new_mask = pygame.mask.Mask(mask_size) + new_mask.set_at(corner) + expected_comps[corner] = new_mask + + center = (mask_size[0] // 2, mask_size[1] // 2) + mask.set_at(center) + + new_mask = pygame.mask.Mask(mask_size) + new_mask.set_at(center) + expected_comps[center] = new_mask + mask_count = mask.count() + + connected_comps = mask.connected_components(-3) + + self.assertEqual(len(connected_comps), len(expected_comps)) + + for comp in connected_comps: + # Since the masks in the connected component list can be in any + # order, loop the expected components to find its match. + found = False + + for pt in tuple(expected_comps.keys()): + if comp.get_at(pt): + found = True + assertMaskEqual(self, comp, expected_comps[pt]) + del expected_comps[pt] # Entry removed so it isn't reused. + break + + self.assertTrue(found, "missing component for pt={}".format(pt)) + + # Ensure the original mask is unchanged. + self.assertEqual(mask.count(), mask_count) + self.assertEqual(mask.get_size(), mask_size) + + def test_get_bounding_rects(self): + """Ensures get_bounding_rects works correctly.""" + # Create masks with different set point groups. Each group of + # connected set points will be contained in its own bounding rect. + # Diagonal points are considered connected. + mask_data = [] # [((size), ((rect1_pts), ...)), ...] + + # Mask 1: + # |0123456789 + # -+---------- + # 0|1100000000 + # 1|1000000000 + # 2|0000000000 + # 3|1001000000 + # 4|0000000000 + # 5|0000000000 + # 6|0000000000 + # 7|0000000000 + # 8|0000000000 + # 9|0000000000 + mask_data.append( + ( + (10, 10), # size + # Points to set for the 3 bounding rects. + (((0, 0), (1, 0), (0, 1)), ((0, 3),), ((3, 3),)), # rect1 # rect2 + ) + ) # rect3 + + # Mask 2: + # |0123 + # -+---- + # 0|1100 + # 1|1111 + mask_data.append( + ( + (4, 2), # size + # Points to set for the 1 bounding rect. + (((0, 0), (1, 0), (0, 1), (1, 1), (2, 1), (3, 1)),), + ) + ) + + # Mask 3: + # |01234 + # -+----- + # 0|00100 + # 1|01110 + # 2|00100 + mask_data.append( + ( + (5, 3), # size + # Points to set for the 1 bounding rect. + (((2, 0), (1, 1), (2, 1), (3, 1), (2, 2)),), + ) + ) + + # Mask 4: + # |01234 + # -+----- + # 0|00010 + # 1|00100 + # 2|01000 + mask_data.append( + ( + (5, 3), # size + # Points to set for the 1 bounding rect. + (((3, 0), (2, 1), (1, 2)),), + ) + ) + + # Mask 5: + # |01234 + # -+----- + # 0|00011 + # 1|11111 + mask_data.append( + ( + (5, 2), # size + # Points to set for the 1 bounding rect. + (((3, 0), (4, 0), (0, 1), (1, 1), (2, 1), (3, 1)),), + ) + ) + + # Mask 6: + # |01234 + # -+----- + # 0|10001 + # 1|00100 + # 2|10001 + mask_data.append( + ( + (5, 3), # size + # Points to set for the 5 bounding rects. + ( + ((0, 0),), # rect1 + ((4, 0),), # rect2 + ((2, 1),), # rect3 + ((0, 2),), # rect4 + ((4, 2),), + ), + ) + ) # rect5 + + for size, rect_point_tuples in mask_data: + rects = [] + mask = pygame.Mask(size) + + for rect_points in rect_point_tuples: + rects.append(create_bounding_rect(rect_points)) + for pt in rect_points: + mask.set_at(pt) + + expected_rects = sorted(rects, key=tuple) + + rects = mask.get_bounding_rects() + + self.assertListEqual( + sorted(mask.get_bounding_rects(), key=tuple), + expected_rects, + "size={}".format(size), + ) + + def test_to_surface(self): + """Ensures empty and full masks can be drawn onto surfaces.""" + expected_ref_count = 3 + size = (33, 65) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + test_fills = ((pygame.Color("white"), True), (pygame.Color("black"), False)) + + for expected_color, fill in test_fills: + surface.fill(surface_color) + mask = pygame.mask.Mask(size, fill=fill) + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__create_surface(self): + """Ensures empty and full masks can be drawn onto a created surface.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + size = (33, 65) + test_fills = ((pygame.Color("white"), True), (pygame.Color("black"), False)) + + for expected_color, fill in test_fills: + mask = pygame.mask.Mask(size, fill=fill) + + for use_arg in (True, False): + if use_arg: + to_surface = mask.to_surface(None) + else: + to_surface = mask.to_surface() + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__surface_param(self): + """Ensures to_surface accepts a surface arg/kwarg.""" + expected_ref_count = 4 + expected_color = pygame.Color("white") + surface_color = pygame.Color("red") + size = (5, 3) + mask = pygame.mask.Mask(size, fill=True) + surface = pygame.Surface(size) + kwargs = {"surface": surface} + + for use_kwargs in (True, False): + surface.fill(surface_color) + + if use_kwargs: + to_surface = mask.to_surface(**kwargs) + else: + to_surface = mask.to_surface(kwargs["surface"]) + + self.assertIs(to_surface, surface) + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__setsurface_param(self): + """Ensures to_surface accepts a setsurface arg/kwarg.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("red") + size = (5, 3) + mask = pygame.mask.Mask(size, fill=True) + setsurface = pygame.Surface(size, expected_flag, expected_depth) + setsurface.fill(expected_color) + kwargs = {"setsurface": setsurface} + + for use_kwargs in (True, False): + if use_kwargs: + to_surface = mask.to_surface(**kwargs) + else: + to_surface = mask.to_surface(None, kwargs["setsurface"]) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetsurface_param(self): + """Ensures to_surface accepts a unsetsurface arg/kwarg.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("red") + size = (5, 3) + mask = pygame.mask.Mask(size) + unsetsurface = pygame.Surface(size, expected_flag, expected_depth) + unsetsurface.fill(expected_color) + kwargs = {"unsetsurface": unsetsurface} + + for use_kwargs in (True, False): + if use_kwargs: + to_surface = mask.to_surface(**kwargs) + else: + to_surface = mask.to_surface(None, None, kwargs["unsetsurface"]) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__setcolor_param(self): + """Ensures to_surface accepts a setcolor arg/kwarg.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("red") + size = (5, 3) + mask = pygame.mask.Mask(size, fill=True) + kwargs = {"setcolor": expected_color} + + for use_kwargs in (True, False): + if use_kwargs: + to_surface = mask.to_surface(**kwargs) + else: + to_surface = mask.to_surface(None, None, None, kwargs["setcolor"]) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__setcolor_default(self): + """Ensures the default setcolor is correct.""" + expected_color = pygame.Color("white") + size = (3, 7) + mask = pygame.mask.Mask(size, fill=True) + + to_surface = mask.to_surface( + surface=None, setsurface=None, unsetsurface=None, unsetcolor=None + ) + + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetcolor_param(self): + """Ensures to_surface accepts a unsetcolor arg/kwarg.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("red") + size = (5, 3) + mask = pygame.mask.Mask(size) + kwargs = {"unsetcolor": expected_color} + + for use_kwargs in (True, False): + if use_kwargs: + to_surface = mask.to_surface(**kwargs) + else: + to_surface = mask.to_surface( + None, None, None, None, kwargs["unsetcolor"] + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetcolor_default(self): + """Ensures the default unsetcolor is correct.""" + expected_color = pygame.Color("black") + size = (3, 7) + mask = pygame.mask.Mask(size) + + to_surface = mask.to_surface( + surface=None, setsurface=None, unsetsurface=None, setcolor=None + ) + + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__dest_param(self): + """Ensures to_surface accepts a dest arg/kwarg.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + default_surface_color = (0, 0, 0, 0) + default_unsetcolor = pygame.Color("black") + dest = (0, 0) + size = (5, 3) + mask = pygame.mask.Mask(size) + kwargs = {"dest": dest} + + for use_kwargs in (True, False): + if use_kwargs: + expected_color = default_unsetcolor + + to_surface = mask.to_surface(**kwargs) + else: + expected_color = default_surface_color + + to_surface = mask.to_surface( + None, None, None, None, None, kwargs["dest"] + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__dest_default(self): + """Ensures the default dest is correct.""" + expected_color = pygame.Color("white") + surface_color = pygame.Color("red") + + mask_size = (3, 2) + mask = pygame.mask.Mask(mask_size, fill=True) + mask_rect = mask.get_rect() + + # Make the surface bigger than the mask. + surf_size = (mask_size[0] + 2, mask_size[1] + 1) + surface = pygame.Surface(surf_size, SRCALPHA, 32) + surface.fill(surface_color) + + to_surface = mask.to_surface( + surface, setsurface=None, unsetsurface=None, unsetcolor=None + ) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), surf_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__kwargs(self): + """Ensures to_surface accepts the correct kwargs.""" + expected_color = pygame.Color("white") + size = (5, 3) + mask = pygame.mask.Mask(size, fill=True) + surface = pygame.Surface(size) + surface_color = pygame.Color("red") + setsurface = surface.copy() + setsurface.fill(expected_color) + + kwargs = { + "surface": surface, + "setsurface": setsurface, + "unsetsurface": surface.copy(), + "setcolor": expected_color, + "unsetcolor": pygame.Color("yellow"), + "dest": (0, 0), + } + + for name in ( + "dest", + "unsetcolor", + "setcolor", + "unsetsurface", + "setsurface", + "surface", + ): + surface.fill(surface_color) # Clear for each test. + + to_surface = mask.to_surface(**kwargs) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + kwargs.pop(name) # Get ready for next loop iteration. + + def test_to_surface__kwargs_create_surface(self): + """Ensures to_surface accepts the correct kwargs + when creating a surface. + """ + expected_color = pygame.Color("black") + size = (5, 3) + mask = pygame.mask.Mask(size) + setsurface = pygame.Surface(size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + unsetsurface = setsurface.copy() + unsetsurface.fill(expected_color) + + kwargs = { + "surface": None, + "setsurface": setsurface, + "unsetsurface": unsetsurface, + "setcolor": pygame.Color("yellow"), + "unsetcolor": expected_color, + "dest": (0, 0), + } + + for name in ( + "dest", + "unsetcolor", + "setcolor", + "unsetsurface", + "setsurface", + "surface", + ): + to_surface = mask.to_surface(**kwargs) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + kwargs.pop(name) # Get ready for next loop iteration. + + def test_to_surface__kwargs_order_independent(self): + """Ensures to_surface kwargs are not order dependent.""" + expected_color = pygame.Color("blue") + size = (3, 2) + mask = pygame.mask.Mask(size, fill=True) + surface = pygame.Surface(size) + + to_surface = mask.to_surface( + dest=(0, 0), + setcolor=expected_color, + unsetcolor=None, + surface=surface, + unsetsurface=pygame.Surface(size), + setsurface=None, + ) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__args_invalid_types(self): + """Ensures to_surface detects invalid kwarg types.""" + size = (3, 2) + mask = pygame.mask.Mask(size, fill=True) + invalid_surf = pygame.Color("green") + invalid_color = pygame.Surface(size) + + with self.assertRaises(TypeError): + # Invalid dest. + to_surface = mask.to_surface(None, None, None, None, None, (0,)) + + with self.assertRaises(TypeError): + # Invalid unsetcolor. + to_surface = mask.to_surface(None, None, None, None, invalid_color) + + with self.assertRaises(TypeError): + # Invalid setcolor. + to_surface = mask.to_surface(None, None, None, invalid_color, None) + + with self.assertRaises(TypeError): + # Invalid unsetsurface. + to_surface = mask.to_surface(None, None, invalid_surf, None, None) + + with self.assertRaises(TypeError): + # Invalid setsurface. + to_surface = mask.to_surface(None, invalid_surf, None, None, None) + + with self.assertRaises(TypeError): + # Invalid surface. + to_surface = mask.to_surface(invalid_surf, None, None, None, None) + + def test_to_surface__kwargs_invalid_types(self): + """Ensures to_surface detects invalid kwarg types.""" + size = (3, 2) + mask = pygame.mask.Mask(size) + + valid_kwargs = { + "surface": pygame.Surface(size), + "setsurface": pygame.Surface(size), + "unsetsurface": pygame.Surface(size), + "setcolor": pygame.Color("green"), + "unsetcolor": pygame.Color("green"), + "dest": (0, 0), + } + + invalid_kwargs = { + "surface": (1, 2, 3, 4), + "setsurface": pygame.Color("green"), + "unsetsurface": ((1, 2), (2, 1)), + "setcolor": (1, 2), + "unsetcolor": pygame.Surface((2, 2)), + "dest": (0, 0, 0), + } + + for kwarg in ( + "surface", + "setsurface", + "unsetsurface", + "setcolor", + "unsetcolor", + "dest", + ): + kwargs = dict(valid_kwargs) + kwargs[kwarg] = invalid_kwargs[kwarg] + + with self.assertRaises(TypeError): + to_surface = mask.to_surface(**kwargs) + + def test_to_surface__kwargs_invalid_name(self): + """Ensures to_surface detects invalid kwarg names.""" + mask = pygame.mask.Mask((3, 2)) + kwargs = {"setcolour": pygame.Color("red")} + + with self.assertRaises(TypeError): + to_surface = mask.to_surface(**kwargs) + + def test_to_surface__args_and_kwargs(self): + """Ensures to_surface accepts a combination of args/kwargs""" + size = (5, 3) + dest = (0, 0) + + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("yellow") + unsetsurface_color = pygame.Color("blue") + setcolor = pygame.Color("green") + unsetcolor = pygame.Color("cyan") + + surface = pygame.Surface(size, SRCALPHA, 32) + setsurface = surface.copy() + unsetsurface = surface.copy() + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + kwargs = { + "surface": surface, + "setsurface": setsurface, + "unsetsurface": unsetsurface, + "setcolor": setcolor, + "unsetcolor": unsetcolor, + "dest": dest, + } + + mask = pygame.mask.Mask(size, fill=True) + expected_color = setsurface_color + + for name in ( + "surface", + "setsurface", + "unsetsurface", + "setcolor", + "unsetcolor", + "dest", + ): + kwargs.pop(name) + + if "surface" == name: + to_surface = mask.to_surface(surface, **kwargs) + elif "setsurface" == name: + to_surface = mask.to_surface(surface, setsurface, **kwargs) + elif "unsetsurface" == name: + to_surface = mask.to_surface( + surface, setsurface, unsetsurface, **kwargs + ) + elif "setcolor" == name: + to_surface = mask.to_surface( + surface, setsurface, unsetsurface, setcolor, **kwargs + ) + elif "unsetcolor" == name: + to_surface = mask.to_surface( + surface, setsurface, unsetsurface, setcolor, unsetcolor, **kwargs + ) + else: + to_surface = mask.to_surface( + surface, + setsurface, + unsetsurface, + setcolor, + unsetcolor, + dest, + **kwargs + ) + + self.assertIs(to_surface, surface) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__valid_setcolor_formats(self): + """Ensures to_surface handles valid setcolor formats correctly.""" + size = (5, 3) + mask = pygame.mask.Mask(size, fill=True) + surface = pygame.Surface(size, SRCALPHA, 32) + green = pygame.Color("green") + greens = ((0, 255, 0), (0, 255, 0, 255), surface.map_rgb(green), green) + + for setcolor in greens: + if isinstance(setcolor, int): + expected_color = surface.unmap_rgb(setcolor) + else: + expected_color = setcolor + + to_surface = mask.to_surface(setcolor=setcolor) + + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__valid_unsetcolor_formats(self): + """Ensures to_surface handles valid unsetcolor formats correctly.""" + size = (5, 3) + mask = pygame.mask.Mask(size) + surface = pygame.Surface(size, SRCALPHA, 32) + green = pygame.Color("green") + greens = ((0, 255, 0), (0, 255, 0, 255), surface.map_rgb(green), green) + + for unsetcolor in greens: + if isinstance(unsetcolor, int): + expected_color = surface.unmap_rgb(unsetcolor) + else: + expected_color = unsetcolor + + to_surface = mask.to_surface(unsetcolor=unsetcolor) + + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__invalid_setcolor_formats(self): + """Ensures to_surface handles invalid setcolor formats correctly.""" + mask = pygame.mask.Mask((5, 3)) + + for setcolor in ("green", "#00FF00FF", "0x00FF00FF"): + with self.assertRaises(TypeError): + to_surface = mask.to_surface(setcolor=setcolor) + + def test_to_surface__invalid_unsetcolor_formats(self): + """Ensures to_surface handles invalid unsetcolor formats correctly.""" + mask = pygame.mask.Mask((5, 3)) + + for unsetcolor in ("green", "#00FF00FF", "0x00FF00FF"): + with self.assertRaises(TypeError): + to_surface = mask.to_surface(unsetcolor=unsetcolor) + + def test_to_surface__valid_dest_formats(self): + """Ensures to_surface handles valid dest formats correctly.""" + expected_color = pygame.Color("white") + mask = pygame.mask.Mask((3, 5), fill=True) + dests = ( + (0, 0), + [0, 0], + Vector2(0, 0), + (0, 0, 100, 100), + pygame.Rect((0, 0), (10, 10)), + ) + + for dest in dests: + to_surface = mask.to_surface(dest=dest) + + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__invalid_dest_formats(self): + """Ensures to_surface handles invalid dest formats correctly.""" + mask = pygame.mask.Mask((3, 5)) + invalid_dests = ( + (0,), # Incorrect size. + (0, 0, 0), # Incorrect size. + set([0, 1]), # Incorrect type. + {0: 1}, # Incorrect type. + Rect, + ) # Incorrect type. + + for dest in invalid_dests: + with self.assertRaises(TypeError): + to_surface = mask.to_surface(dest=dest) + + def test_to_surface__negative_sized_dest_rect(self): + """Ensures to_surface correctly handles negative sized dest rects.""" + expected_color = pygame.Color("white") + mask = pygame.mask.Mask((3, 5), fill=True) + dests = ( + pygame.Rect((0, 0), (10, -10)), + pygame.Rect((0, 0), (-10, 10)), + pygame.Rect((0, 0), (-10, -10)), + ) + + for dest in dests: + to_surface = mask.to_surface(dest=dest) + + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__zero_sized_dest_rect(self): + """Ensures to_surface correctly handles zero sized dest rects.""" + expected_color = pygame.Color("white") + mask = pygame.mask.Mask((3, 5), fill=True) + dests = ( + pygame.Rect((0, 0), (0, 10)), + pygame.Rect((0, 0), (10, 0)), + pygame.Rect((0, 0), (0, 0)), + ) + + for dest in dests: + to_surface = mask.to_surface(dest=dest) + + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__default_surface_with_param_combinations(self): + """Ensures to_surface works with a default surface value + and combinations of other parameters. + + This tests many different parameter combinations with full and empty + masks. + """ + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + size = (5, 3) + dest = (0, 0) + + default_surface_color = (0, 0, 0, 0) + setsurface_color = pygame.Color("yellow") + unsetsurface_color = pygame.Color("blue") + setcolor = pygame.Color("green") + unsetcolor = pygame.Color("cyan") + + setsurface = pygame.Surface(size, expected_flag, expected_depth) + unsetsurface = setsurface.copy() + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + kwargs = { + "setsurface": None, + "unsetsurface": None, + "setcolor": None, + "unsetcolor": None, + "dest": None, + } + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + + # Test different combinations of parameters. + for setsurface_param in (setsurface, None): + kwargs["setsurface"] = setsurface_param + + for unsetsurface_param in (unsetsurface, None): + kwargs["unsetsurface"] = unsetsurface_param + + for setcolor_param in (setcolor, None): + kwargs["setcolor"] = setcolor_param + + for unsetcolor_param in (unsetcolor, None): + kwargs["unsetcolor"] = unsetcolor_param + + for dest_param in (dest, None): + if dest_param is None: + kwargs.pop("dest", None) + else: + kwargs["dest"] = dest_param + + if fill: + if setsurface_param is not None: + expected_color = setsurface_color + elif setcolor_param is not None: + expected_color = setcolor + else: + expected_color = default_surface_color + else: + if unsetsurface_param is not None: + expected_color = unsetsurface_color + elif unsetcolor_param is not None: + expected_color = unsetcolor + else: + expected_color = default_surface_color + + to_surface = mask.to_surface(**kwargs) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual( + sys.getrefcount(to_surface), expected_ref_count + ) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual( + to_surface.get_bitsize(), expected_depth + ) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__surface_with_param_combinations(self): + """Ensures to_surface works with a surface value + and combinations of other parameters. + + This tests many different parameter combinations with full and empty + masks. + """ + expected_ref_count = 4 + expected_flag = SRCALPHA + expected_depth = 32 + size = (5, 3) + dest = (0, 0) + + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("yellow") + unsetsurface_color = pygame.Color("blue") + setcolor = pygame.Color("green") + unsetcolor = pygame.Color("cyan") + + surface = pygame.Surface(size, expected_flag, expected_depth) + setsurface = surface.copy() + unsetsurface = surface.copy() + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + kwargs = { + "surface": surface, + "setsurface": None, + "unsetsurface": None, + "setcolor": None, + "unsetcolor": None, + "dest": None, + } + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + + # Test different combinations of parameters. + for setsurface_param in (setsurface, None): + kwargs["setsurface"] = setsurface_param + + for unsetsurface_param in (unsetsurface, None): + kwargs["unsetsurface"] = unsetsurface_param + + for setcolor_param in (setcolor, None): + kwargs["setcolor"] = setcolor_param + + for unsetcolor_param in (unsetcolor, None): + kwargs["unsetcolor"] = unsetcolor_param + surface.fill(surface_color) # Clear for each test. + + for dest_param in (dest, None): + if dest_param is None: + kwargs.pop("dest", None) + else: + kwargs["dest"] = dest_param + + if fill: + if setsurface_param is not None: + expected_color = setsurface_color + elif setcolor_param is not None: + expected_color = setcolor + else: + expected_color = surface_color + else: + if unsetsurface_param is not None: + expected_color = unsetsurface_color + elif unsetcolor_param is not None: + expected_color = unsetcolor + else: + expected_color = surface_color + + to_surface = mask.to_surface(**kwargs) + + self.assertIs(to_surface, surface) + self.assertEqual( + sys.getrefcount(to_surface), expected_ref_count + ) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual( + to_surface.get_bitsize(), expected_depth + ) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__set_and_unset_bits(self): + """Ensures that to_surface works correctly with with set/unset bits + when using the defaults for setcolor and unsetcolor. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + width, height = size = (10, 20) + mask = pygame.mask.Mask(size) + mask_rect = mask.get_rect() + + surface = pygame.Surface(size) + surface_color = pygame.Color("red") + + # Create a checkerboard pattern of set/unset bits. + for pos in ((x, y) for x in range(width) for y in range(x & 1, height, 2)): + mask.set_at(pos) + + # Test different dest values. + for dest in self.ORIGIN_OFFSETS: + mask_rect.topleft = dest + surface.fill(surface_color) + + to_surface = mask.to_surface(surface, dest=dest) + + to_surface.lock() # Lock for possible speed up. + for pos in ((x, y) for x in range(width) for y in range(height)): + mask_pos = (pos[0] - dest[0], pos[1] - dest[1]) + if not mask_rect.collidepoint(pos): + expected_color = surface_color + elif mask.get_at(mask_pos): + expected_color = default_setcolor + else: + expected_color = default_unsetcolor + + self.assertEqual(to_surface.get_at(pos), expected_color, (dest, pos)) + to_surface.unlock() + + def test_to_surface__set_and_unset_bits_with_setsurface_unsetsurface(self): + """Ensures that to_surface works correctly with with set/unset bits + when using setsurface and unsetsurface. + """ + width, height = size = (10, 20) + mask = pygame.mask.Mask(size) + mask_rect = mask.get_rect() + + surface = pygame.Surface(size) + surface_color = pygame.Color("red") + + setsurface = surface.copy() + setsurface_color = pygame.Color("green") + setsurface.fill(setsurface_color) + + unsetsurface = surface.copy() + unsetsurface_color = pygame.Color("blue") + unsetsurface.fill(unsetsurface_color) + + # Create a checkerboard pattern of set/unset bits. + for pos in ((x, y) for x in range(width) for y in range(x & 1, height, 2)): + mask.set_at(pos) + + # Test different dest values. + for dest in self.ORIGIN_OFFSETS: + mask_rect.topleft = dest + + # Tests the color parameters set to None and also as their + # default values. Should have no effect as they are not being + # used, but this exercises different to_surface() code. + for disable_color_params in (True, False): + surface.fill(surface_color) # Clear for each test. + + if disable_color_params: + to_surface = mask.to_surface( + surface, + dest=dest, + setsurface=setsurface, + unsetsurface=unsetsurface, + setcolor=None, + unsetcolor=None, + ) + else: + to_surface = mask.to_surface( + surface, + dest=dest, + setsurface=setsurface, + unsetsurface=unsetsurface, + ) + + to_surface.lock() # Lock for possible speed up. + + for pos in ((x, y) for x in range(width) for y in range(height)): + mask_pos = (pos[0] - dest[0], pos[1] - dest[1]) + + if not mask_rect.collidepoint(pos): + expected_color = surface_color + elif mask.get_at(mask_pos): + expected_color = setsurface_color + else: + expected_color = unsetsurface_color + + self.assertEqual(to_surface.get_at(pos), expected_color) + to_surface.unlock() + + def test_to_surface__surface_narrower_than_mask(self): + """Ensures that surfaces narrower than the mask work correctly. + + For this test the surface's width is less than the mask's width. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 20) + narrow_size = (6, 20) + + surface = pygame.Surface(narrow_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), narrow_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__setsurface_narrower_than_mask(self): + """Ensures that setsurfaces narrower than the mask work correctly. + + For this test the setsurface's width is less than the mask's width. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 20) + narrow_size = (6, 20) + + setsurface = pygame.Surface(narrow_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_setcolor, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_unsetcolor) + + def test_to_surface__unsetsurface_narrower_than_mask(self): + """Ensures that unsetsurfaces narrower than the mask work correctly. + + For this test the unsetsurface's width is less than the mask's width. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 20) + narrow_size = (6, 20) + + unsetsurface = pygame.Surface(narrow_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_setcolor) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_unsetcolor, unsetsurface_rect + ) + + def test_to_surface__setsurface_narrower_than_mask_and_colors_none(self): + """Ensures that setsurfaces narrower than the mask work correctly + when setcolor and unsetcolor are set to None. + + For this test the setsurface's width is less than the mask's width. + """ + default_surface_color = (0, 0, 0, 0) + mask_size = (10, 20) + narrow_size = (6, 20) + + setsurface = pygame.Surface(narrow_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface( + setsurface=setsurface, setcolor=None, unsetcolor=None + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_surface_color, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_surface_color) + + def test_to_surface__unsetsurface_narrower_than_mask_and_colors_none(self): + """Ensures that unsetsurfaces narrower than the mask work correctly + when setcolor and unsetcolor are set to None. + + For this test the unsetsurface's width is less than the mask's width. + """ + default_surface_color = (0, 0, 0, 0) + mask_size = (10, 20) + narrow_size = (6, 20) + + unsetsurface = pygame.Surface(narrow_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface( + unsetsurface=unsetsurface, setcolor=None, unsetcolor=None + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_surface_color) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_surface_color, unsetsurface_rect + ) + + def test_to_surface__surface_wider_than_mask(self): + """Ensures that surfaces wider than the mask work correctly. + + For this test the surface's width is greater than the mask's width. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (6, 15) + wide_size = (11, 15) + + surface = pygame.Surface(wide_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), wide_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_wider_than_mask(self): + """Ensures that setsurfaces wider than the mask work correctly. + + For this test the setsurface's width is greater than the mask's width. + """ + default_unsetcolor = pygame.Color("black") + mask_size = (6, 15) + wide_size = (11, 15) + + setsurface = pygame.Surface(wide_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = setsurface_color if fill else default_unsetcolor + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetsurface_wider_than_mask(self): + """Ensures that unsetsurfaces wider than the mask work correctly. + + For this test the unsetsurface's width is greater than the mask's + width. + """ + default_setcolor = pygame.Color("white") + mask_size = (6, 15) + wide_size = (11, 15) + + unsetsurface = pygame.Surface(wide_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = default_setcolor if fill else unsetsurface_color + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__surface_shorter_than_mask(self): + """Ensures that surfaces shorter than the mask work correctly. + + For this test the surface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 11) + short_size = (10, 6) + + surface = pygame.Surface(short_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), short_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__setsurface_shorter_than_mask(self): + """Ensures that setsurfaces shorter than the mask work correctly. + + For this test the setsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 11) + short_size = (10, 6) + + setsurface = pygame.Surface(short_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_setcolor, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_unsetcolor) + + def test_to_surface__unsetsurface_shorter_than_mask(self): + """Ensures that unsetsurfaces shorter than the mask work correctly. + + For this test the unsetsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 11) + short_size = (10, 6) + + unsetsurface = pygame.Surface(short_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_setcolor) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_unsetcolor, unsetsurface_rect + ) + + def test_to_surface__setsurface_shorter_than_mask_and_colors_none(self): + """Ensures that setsurfaces shorter than the mask work correctly + when setcolor and unsetcolor are set to None. + + For this test the setsurface's height is less than the mask's height. + """ + default_surface_color = (0, 0, 0, 0) + mask_size = (10, 11) + short_size = (10, 6) + + setsurface = pygame.Surface(short_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface( + setsurface=setsurface, setcolor=None, unsetcolor=None + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_surface_color, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_surface_color) + + def test_to_surface__unsetsurface_shorter_than_mask_and_colors_none(self): + """Ensures that unsetsurfaces shorter than the mask work correctly + when setcolor and unsetcolor are set to None. + + For this test the unsetsurface's height is less than the mask's height. + """ + default_surface_color = (0, 0, 0, 0) + mask_size = (10, 11) + short_size = (10, 6) + + unsetsurface = pygame.Surface(short_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface( + unsetsurface=unsetsurface, setcolor=None, unsetcolor=None + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_surface_color) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_surface_color, unsetsurface_rect + ) + + def test_to_surface__surface_taller_than_mask(self): + """Ensures that surfaces taller than the mask work correctly. + + For this test the surface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 6) + tall_size = (10, 11) + + surface = pygame.Surface(tall_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), tall_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_taller_than_mask(self): + """Ensures that setsurfaces taller than the mask work correctly. + + For this test the setsurface's height is greater than the mask's + height. + """ + default_unsetcolor = pygame.Color("black") + mask_size = (10, 6) + tall_size = (10, 11) + + setsurface = pygame.Surface(tall_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = setsurface_color if fill else default_unsetcolor + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetsurface_taller_than_mask(self): + """Ensures that unsetsurfaces taller than the mask work correctly. + + For this test the unsetsurface's height is greater than the mask's + height. + """ + default_setcolor = pygame.Color("white") + mask_size = (10, 6) + tall_size = (10, 11) + + unsetsurface = pygame.Surface(tall_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = default_setcolor if fill else unsetsurface_color + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__surface_wider_and_taller_than_mask(self): + """Ensures that surfaces wider and taller than the mask work correctly. + + For this test the surface's width is greater than the mask's width and + the surface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (6, 8) + wide_tall_size = (11, 15) + + surface = pygame.Surface(wide_tall_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), wide_tall_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_wider_and_taller_than_mask(self): + """Ensures that setsurfaces wider and taller than the mask work + correctly. + + For this test the setsurface's width is greater than the mask's width + and the setsurface's height is greater than the mask's height. + """ + default_unsetcolor = pygame.Color("black") + mask_size = (6, 8) + wide_tall_size = (11, 15) + + setsurface = pygame.Surface(wide_tall_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = setsurface_color if fill else default_unsetcolor + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetsurface_wider_and_taller_than_mask(self): + """Ensures that unsetsurfaces wider and taller than the mask work + correctly. + + For this test the unsetsurface's width is greater than the mask's width + and the unsetsurface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + mask_size = (6, 8) + wide_tall_size = (11, 15) + + unsetsurface = pygame.Surface(wide_tall_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = default_setcolor if fill else unsetsurface_color + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__surface_wider_and_shorter_than_mask(self): + """Ensures that surfaces wider and shorter than the mask work + correctly. + + For this test the surface's width is greater than the mask's width and + the surface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (7, 11) + wide_short_size = (13, 6) + + surface = pygame.Surface(wide_short_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), wide_short_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_wider_and_shorter_than_mask(self): + """Ensures that setsurfaces wider and shorter than the mask work + correctly. + + For this test the setsurface's width is greater than the mask's width + and the setsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (7, 11) + wide_short_size = (10, 6) + + setsurface = pygame.Surface(wide_short_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_setcolor, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_unsetcolor) + + def test_to_surface__unsetsurface_wider_and_shorter_than_mask(self): + """Ensures that unsetsurfaces wider and shorter than the mask work + correctly. + + For this test the unsetsurface's width is greater than the mask's width + and the unsetsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (7, 11) + wide_short_size = (10, 6) + + unsetsurface = pygame.Surface(wide_short_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_setcolor) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_unsetcolor, unsetsurface_rect + ) + + def test_to_surface__surface_narrower_and_taller_than_mask(self): + """Ensures that surfaces narrower and taller than the mask work + correctly. + + For this test the surface's width is less than the mask's width and + the surface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 8) + narrow_tall_size = (6, 15) + + surface = pygame.Surface(narrow_tall_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), narrow_tall_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_narrower_and_taller_than_mask(self): + """Ensures that setsurfaces narrower and taller than the mask work + correctly. + + For this test the setsurface's width is less than the mask's width + and the setsurface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 8) + narrow_tall_size = (6, 15) + + setsurface = pygame.Surface(narrow_tall_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_setcolor, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_unsetcolor) + + def test_to_surface__unsetsurface_narrower_and_taller_than_mask(self): + """Ensures that unsetsurfaces narrower and taller than the mask work + correctly. + + For this test the unsetsurface's width is less than the mask's width + and the unsetsurface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 8) + narrow_tall_size = (6, 15) + + unsetsurface = pygame.Surface(narrow_tall_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = default_setcolor if fill else unsetsurface_color + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_setcolor) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_unsetcolor, unsetsurface_rect + ) + + def test_to_surface__surface_narrower_and_shorter_than_mask(self): + """Ensures that surfaces narrower and shorter than the mask work + correctly. + + For this test the surface's width is less than the mask's width and + the surface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 18) + narrow_short_size = (6, 15) + + surface = pygame.Surface(narrow_short_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), narrow_short_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_narrower_and_shorter_than_mask(self): + """Ensures that setsurfaces narrower and shorter than the mask work + correctly. + + For this test the setsurface's width is less than the mask's width + and the setsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 18) + narrow_short_size = (6, 15) + + setsurface = pygame.Surface(narrow_short_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_setcolor, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_unsetcolor) + + def test_to_surface__unsetsurface_narrower_and_shorter_than_mask(self): + """Ensures that unsetsurfaces narrower and shorter than the mask work + correctly. + + For this test the unsetsurface's width is less than the mask's width + and the unsetsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 18) + narrow_short_size = (6, 15) + + unsetsurface = pygame.Surface(narrow_short_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = default_setcolor if fill else unsetsurface_color + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_setcolor) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_unsetcolor, unsetsurface_rect + ) + + def test_to_surface__all_surfaces_different_sizes_than_mask(self): + """Ensures that all the surface parameters can be of different sizes. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + + mask_size = (10, 15) + surface_size = (11, 14) + setsurface_size = (9, 8) + unsetsurface_size = (12, 16) + + surface = pygame.Surface(surface_size) + setsurface = pygame.Surface(setsurface_size) + unsetsurface = pygame.Surface(unsetsurface_size) + + surface.fill(surface_color) + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + surface_rect = surface.get_rect() + setsurface_rect = setsurface.get_rect() + unsetsurface_rect = unsetsurface.get_rect() + + # Create a mask that is filled except for a rect in the center. + mask = pygame.mask.Mask(mask_size, fill=True) + mask_rect = mask.get_rect() + unfilled_rect = pygame.Rect((0, 0), (4, 5)) + unfilled_rect.center = mask_rect.center + + for pos in ( + (x, y) + for x in range(unfilled_rect.x, unfilled_rect.w) + for y in range(unfilled_rect.y, unfilled_rect.h) + ): + mask.set_at(pos, 0) + + to_surface = mask.to_surface(surface, setsurface, unsetsurface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), surface_size) + + # Check each surface pixel for the correct color. + to_surface.lock() # Lock for possible speed up. + + for pos in ( + (x, y) for x in range(surface_rect.w) for y in range(surface_rect.h) + ): + if not mask_rect.collidepoint(pos): + expected_color = surface_color + elif mask.get_at(pos): + # Checking set bit colors. + if setsurface_rect.collidepoint(pos): + expected_color = setsurface_color + else: + expected_color = default_setcolor + else: + # Checking unset bit colors. + if unsetsurface_rect.collidepoint(pos): + expected_color = unsetsurface_color + else: + expected_color = default_unsetcolor + + self.assertEqual(to_surface.get_at(pos), expected_color) + + to_surface.unlock() + + def test_to_surface__surface_color_alphas(self): + """Ensures the setsurface/unsetsurface color alpha values are respected. + """ + size = (13, 17) + setsurface_color = pygame.Color("green") + setsurface_color.a = 53 + unsetsurface_color = pygame.Color("blue") + unsetsurface_color.a = 109 + + setsurface = pygame.Surface(size, flags=SRCALPHA, depth=32) + unsetsurface = pygame.Surface(size, flags=SRCALPHA, depth=32) + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + expected_color = setsurface_color if fill else unsetsurface_color + + to_surface = mask.to_surface( + setsurface=setsurface, unsetsurface=unsetsurface + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__color_alphas(self): + """Ensures the setcolor/unsetcolor alpha values are respected.""" + size = (13, 17) + setcolor = pygame.Color("green") + setcolor.a = 35 + unsetcolor = pygame.Color("blue") + unsetcolor.a = 213 + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + expected_color = setcolor if fill else unsetcolor + + to_surface = mask.to_surface(setcolor=setcolor, unsetcolor=unsetcolor) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__depths(self): + """Ensures to_surface works correctly with supported surface depths.""" + size = (13, 17) + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + + for depth in (8, 16, 24, 32): + surface = pygame.Surface(size, depth=depth) + setsurface = pygame.Surface(size, depth=depth) + unsetsurface = pygame.Surface(size, depth=depth) + + surface.fill(surface_color) + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + + # For non-32 bit depths, the actual color can be different from + # what was filled. + expected_color = ( + setsurface.get_at((0, 0)) if fill else unsetsurface.get_at((0, 0)) + ) + + to_surface = mask.to_surface(surface, setsurface, unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__different_depths(self): + """Ensures an exception is raised when surfaces have different depths. + """ + size = (13, 17) + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + mask = pygame.mask.Mask(size) + + # Test different combinations of depths. + test_depths = ( + (8, 8, 16), # surface/setsurface/unsetsurface + (8, 8, 24), + (8, 8, 32), + (16, 16, 24), + (16, 16, 32), + (24, 16, 8), + (32, 16, 16), + (32, 32, 16), + (32, 24, 32), + ) + + for depths in test_depths: + surface = pygame.Surface(size, depth=depths[0]) + setsurface = pygame.Surface(size, depth=depths[1]) + unsetsurface = pygame.Surface(size, depth=depths[2]) + + surface.fill(surface_color) + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + with self.assertRaises(ValueError): + to_surface = mask.to_surface(surface, setsurface, unsetsurface) + + def test_to_surface__different_depths_with_created_surfaces(self): + """Ensures an exception is raised when surfaces have different depths + than the created surface. + """ + size = (13, 17) + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + mask = pygame.mask.Mask(size) + + # Test different combinations of depths. The created surface always has + # a depth of 32. + test_depths = ( + (8, 8), # setsurface/unsetsurface + (16, 16), + (24, 24), + (24, 16), + (32, 8), + (32, 16), + (32, 24), + (16, 32), + ) + + for set_depth, unset_depth in test_depths: + setsurface = pygame.Surface(size, depth=set_depth) + unsetsurface = pygame.Surface(size, depth=unset_depth) + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + with self.assertRaises(ValueError): + to_surface = mask.to_surface( + setsurface=setsurface, unsetsurface=unsetsurface + ) + + def test_to_surface__same_srcalphas(self): + """Ensures to_surface works correctly when the SRCALPHA flag is set or not. + """ + size = (13, 17) + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + + for depth in (16, 32): + for flags in (0, SRCALPHA): + surface = pygame.Surface(size, flags=flags, depth=depth) + setsurface = pygame.Surface(size, flags=flags, depth=depth) + unsetsurface = pygame.Surface(size, flags=flags, depth=depth) + + surface.fill(surface_color) + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + expected_color = setsurface_color if fill else unsetsurface_color + + to_surface = mask.to_surface(surface, setsurface, unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + if flags: + self.assertTrue(to_surface.get_flags() & flags) + + def test_to_surface__same_srcalphas_with_created_surfaces(self): + """Ensures to_surface works correctly when it creates a surface + and the SRCALPHA flag is set on both setsurface and unsetsurface. + """ + size = (13, 17) + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + # The created surface always has a depth of 32 and the SRCALPHA flag set. + expected_flags = SRCALPHA + + setsurface = pygame.Surface(size, flags=expected_flags, depth=32) + unsetsurface = pygame.Surface(size, flags=expected_flags, depth=32) + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + expected_color = setsurface_color if fill else unsetsurface_color + + to_surface = mask.to_surface( + setsurface=setsurface, unsetsurface=unsetsurface + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + self.assertTrue(to_surface.get_flags() & expected_flags) + + def test_to_surface__different_srcalphas(self): + """Ensures an exception is raised when surfaces have different SRCALPHA + flag settings. + """ + size = (13, 17) + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + mask = pygame.mask.Mask(size) + + # Test different combinations of SRCALPHA flags. + test_flags = ( + (SRCALPHA, 0, 0), # surface/setsurface/unsetsurface + (SRCALPHA, SRCALPHA, 0), + (0, SRCALPHA, SRCALPHA), + (0, 0, SRCALPHA), + ) + + for depth in (16, 32): + for flags in test_flags: + surface = pygame.Surface(size, flags=flags[0], depth=depth) + setsurface = pygame.Surface(size, flags=flags[1], depth=depth) + unsetsurface = pygame.Surface(size, flags=flags[2], depth=depth) + + surface.fill(surface_color) + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + with self.assertRaises(ValueError): + to_surface = mask.to_surface(surface, setsurface, unsetsurface) + + def test_to_surface__different_srcalphas_with_created_surfaces(self): + """Ensures an exception is raised when surfaces have different SRCALPHA + flag settings than the created surface. + """ + size = (13, 17) + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + mask = pygame.mask.Mask(size) + + for depth in (16, 32): + # Test different combinations of SRCALPHA flags. The created + # surface always has the SRCALPHA flag set. + for flags in ((0, 0), (SRCALPHA, 0), (0, SRCALPHA)): + setsurface = pygame.Surface(size, flags=flags[0], depth=depth) + unsetsurface = pygame.Surface(size, flags=flags[1], depth=depth) + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + with self.assertRaises(ValueError): + to_surface = mask.to_surface( + setsurface=setsurface, unsetsurface=unsetsurface + ) + + def test_to_surface__dest_on_surface(self): + """Ensures dest parameters on the surface work correctly + when using the defaults for setcolor and unsetcolor. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + width, height = size = (5, 9) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + expected_color = default_setcolor if fill else default_unsetcolor + + # Test the dest parameter at different locations on the surface. + for dest in ((x, y) for y in range(height) for x in range(width)): + surface.fill(surface_color) # Clear for each test. + mask_rect.topleft = dest + + to_surface = mask.to_surface(surface, dest=dest) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, mask_rect + ) + + def test_to_surface__dest_on_surface_with_setsurface_unsetsurface(self): + """Ensures dest parameters on the surface work correctly + when using setsurface and unsetsurface. + """ + width, height = size = (5, 9) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + setsurface = surface.copy() + setsurface_color = pygame.Color("green") + setsurface.fill(setsurface_color) + + unsetsurface = surface.copy() + unsetsurface_color = pygame.Color("blue") + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + expected_color = setsurface_color if fill else unsetsurface_color + + # Test the dest parameter at different locations on the surface. + for dest in ((x, y) for y in range(height) for x in range(width)): + mask_rect.topleft = dest + + # Tests the color parameters set to None and also as their + # default values. Should have no effect as they are not being + # used, but this exercises different to_surface() code. + for disable_color_params in (True, False): + surface.fill(surface_color) # Clear for each test. + + if disable_color_params: + to_surface = mask.to_surface( + surface, + dest=dest, + setsurface=setsurface, + unsetsurface=unsetsurface, + setcolor=None, + unsetcolor=None, + ) + else: + to_surface = mask.to_surface( + surface, + dest=dest, + setsurface=setsurface, + unsetsurface=unsetsurface, + ) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, mask_rect + ) + + def test_to_surface__dest_off_surface(self): + """Ensures dest parameters off the surface work correctly + when using the defaults for setcolor and unsetcolor. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + width, height = size = (5, 7) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + # Test different dests off the surface. + dests = ( + (-1, -1), + (-1, 0), + (0, -1), + (width + 1, 0), + (width + 1, -1), + (width, -1), + (0, height + 1), + (-1, height + 1), + (-1, height), + (width + 1, height + 1), + (width, height + 1), + (width + 1, height), + (-width, -height), + (-width, 0), + (0, -height), + ) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + expected_color = default_setcolor if fill else default_unsetcolor + + for dest in dests: + surface.fill(surface_color) # Clear for each test. + mask_rect.topleft = dest + + to_surface = mask.to_surface(surface, dest=dest) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, mask_rect + ) + + def test_to_surface__dest_off_surface_with_setsurface_unsetsurface(self): + """Ensures dest parameters off the surface work correctly + when using setsurface and unsetsurface. + """ + width, height = size = (5, 7) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + setsurface = surface.copy() + setsurface_color = pygame.Color("green") + setsurface.fill(setsurface_color) + + unsetsurface = surface.copy() + unsetsurface_color = pygame.Color("blue") + unsetsurface.fill(unsetsurface_color) + + # Test different dests off the surface. + dests = ( + (-1, -1), + (-1, 0), + (0, -1), + (width + 1, 0), + (width + 1, -1), + (width, -1), + (0, height + 1), + (-1, height + 1), + (-1, height), + (width + 1, height + 1), + (width, height + 1), + (width + 1, height), + (-width, -height), + (-width, 0), + (0, -height), + ) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + expected_color = setsurface_color if fill else unsetsurface_color + + for dest in dests: + mask_rect.topleft = dest + + # Tests the color parameters set to None and also as their + # default values. Should have no effect as they are not being + # used, but this exercises different to_surface() code. + for disable_color_params in (True, False): + surface.fill(surface_color) # Clear for each test. + + if disable_color_params: + to_surface = mask.to_surface( + surface, + dest=dest, + setsurface=setsurface, + unsetsurface=unsetsurface, + setcolor=None, + unsetcolor=None, + ) + else: + to_surface = mask.to_surface( + surface, + dest=dest, + setsurface=setsurface, + unsetsurface=unsetsurface, + ) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, mask_rect + ) + + def test_to_surface__surface_with_zero_size(self): + """Ensures zero sized surfaces are handled correctly.""" + expected_ref_count = 3 + size = (0, 0) + surface = pygame.Surface(size) + mask = pygame.mask.Mask((3, 4), fill=True) + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertEqual(to_surface.get_size(), size) + + def test_to_surface__setsurface_with_zero_size(self): + """Ensures zero sized setsurfaces are handled correctly.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("white") # Default setcolor. + mask_size = (2, 4) + mask = pygame.mask.Mask(mask_size, fill=True) + setsurface = pygame.Surface((0, 0), expected_flag, expected_depth) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetsurface_with_zero_size(self): + """Ensures zero sized unsetsurfaces are handled correctly.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("black") # Default unsetcolor. + mask_size = (4, 2) + mask = pygame.mask.Mask(mask_size) + unsetsurface = pygame.Surface((0, 0), expected_flag, expected_depth) + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_zero_mask(self): + """Ensures masks can be created with zero sizes.""" + for size in ((100, 0), (0, 100), (0, 0)): + for fill in (True, False): + msg = "size={}, fill={}".format(size, fill) + + mask = pygame.mask.Mask(size, fill=fill) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), size, msg) + + def test_zero_mask_copy(self): + """Ensures copy correctly handles zero sized masks.""" + for expected_size in ((11, 0), (0, 11), (0, 0)): + mask = pygame.mask.Mask(expected_size) + + mask_copy = mask.copy() + + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + + def test_zero_mask_get_size(self): + """Ensures get_size correctly handles zero sized masks.""" + for expected_size in ((41, 0), (0, 40), (0, 0)): + mask = pygame.mask.Mask(expected_size) + + size = mask.get_size() + + self.assertEqual(size, expected_size) + + def test_zero_mask_get_rect(self): + """Ensures get_rect correctly handles zero sized masks.""" + for expected_size in ((4, 0), (0, 4), (0, 0)): + expected_rect = pygame.Rect((0, 0), expected_size) + mask = pygame.mask.Mask(expected_size) + + rect = mask.get_rect() + + self.assertEqual(rect, expected_rect) + + def test_zero_mask_get_at(self): + """Ensures get_at correctly handles zero sized masks.""" + for size in ((51, 0), (0, 50), (0, 0)): + mask = pygame.mask.Mask(size) + + with self.assertRaises(IndexError): + value = mask.get_at((0, 0)) + + def test_zero_mask_set_at(self): + """Ensures set_at correctly handles zero sized masks.""" + for size in ((31, 0), (0, 30), (0, 0)): + mask = pygame.mask.Mask(size) + + with self.assertRaises(IndexError): + mask.set_at((0, 0)) + + def test_zero_mask_overlap(self): + """Ensures overlap correctly handles zero sized masks. + + Tests combinations of sized and zero sized masks. + """ + offset = (0, 0) + + for size1, size2 in zero_size_pairs(51, 42): + msg = "size1={}, size2={}".format(size1, size2) + mask1 = pygame.mask.Mask(size1, fill=True) + mask2 = pygame.mask.Mask(size2, fill=True) + + overlap_pos = mask1.overlap(mask2, offset) + + self.assertIsNone(overlap_pos, msg) + + def test_zero_mask_overlap_area(self): + """Ensures overlap_area correctly handles zero sized masks. + + Tests combinations of sized and zero sized masks. + """ + offset = (0, 0) + expected_count = 0 + + for size1, size2 in zero_size_pairs(41, 52): + msg = "size1={}, size2={}".format(size1, size2) + mask1 = pygame.mask.Mask(size1, fill=True) + mask2 = pygame.mask.Mask(size2, fill=True) + + overlap_count = mask1.overlap_area(mask2, offset) + + self.assertEqual(overlap_count, expected_count, msg) + + def test_zero_mask_overlap_mask(self): + """Ensures overlap_mask correctly handles zero sized masks. + + Tests combinations of sized and zero sized masks. + """ + offset = (0, 0) + expected_count = 0 + + for size1, size2 in zero_size_pairs(43, 53): + msg = "size1={}, size2={}".format(size1, size2) + mask1 = pygame.mask.Mask(size1, fill=True) + mask2 = pygame.mask.Mask(size2, fill=True) + + overlap_mask = mask1.overlap_mask(mask2, offset) + + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) + self.assertEqual(overlap_mask.count(), expected_count, msg) + self.assertEqual(overlap_mask.get_size(), size1, msg) + + def test_zero_mask_fill(self): + """Ensures fill correctly handles zero sized masks.""" + expected_count = 0 + + for size in ((100, 0), (0, 100), (0, 0)): + mask = pygame.mask.Mask(size) + + mask.fill() + + self.assertEqual(mask.count(), expected_count, "size={}".format(size)) + + def test_zero_mask_clear(self): + sizes = ((100, 0), (0, 100), (0, 0)) + + for size in sizes: + mask = pygame.mask.Mask(size) + mask.clear() + self.assertEqual(mask.count(), 0) + + def test_zero_mask_flip(self): + sizes = ((100, 0), (0, 100), (0, 0)) + + for size in sizes: + mask = pygame.mask.Mask(size) + mask.invert() + self.assertEqual(mask.count(), 0) + + def test_zero_mask_scale(self): + sizes = ((100, 0), (0, 100), (0, 0)) + + for size in sizes: + mask = pygame.mask.Mask(size) + mask2 = mask.scale((2, 3)) + + self.assertIsInstance(mask2, pygame.mask.Mask) + self.assertEqual(mask2.get_size(), (2, 3)) + + def test_zero_mask_draw(self): + """Ensures draw correctly handles zero sized masks. + + Tests combinations of sized and zero sized masks. + """ + offset = (0, 0) + + for size1, size2 in zero_size_pairs(31, 37): + msg = "size1={}, size2={}".format(size1, size2) + mask1 = pygame.mask.Mask(size1, fill=True) + mask2 = pygame.mask.Mask(size2, fill=True) + expected_count = mask1.count() + + mask1.draw(mask2, offset) + + self.assertEqual(mask1.count(), expected_count, msg) + self.assertEqual(mask1.get_size(), size1, msg) + + def test_zero_mask_erase(self): + """Ensures erase correctly handles zero sized masks. + + Tests combinations of sized and zero sized masks. + """ + offset = (0, 0) + + for size1, size2 in zero_size_pairs(29, 23): + msg = "size1={}, size2={}".format(size1, size2) + mask1 = pygame.mask.Mask(size1, fill=True) + mask2 = pygame.mask.Mask(size2, fill=True) + expected_count = mask1.count() + + mask1.erase(mask2, offset) + + self.assertEqual(mask1.count(), expected_count, msg) + self.assertEqual(mask1.get_size(), size1, msg) + + def test_zero_mask_count(self): + sizes = ((100, 0), (0, 100), (0, 0)) + + for size in sizes: + mask = pygame.mask.Mask(size, fill=True) + self.assertEqual(mask.count(), 0) + + def test_zero_mask_centroid(self): + sizes = ((100, 0), (0, 100), (0, 0)) + + for size in sizes: + mask = pygame.mask.Mask(size) + self.assertEqual(mask.centroid(), (0, 0)) + + def test_zero_mask_angle(self): + sizes = ((100, 0), (0, 100), (0, 0)) + + for size in sizes: + mask = pygame.mask.Mask(size) + self.assertEqual(mask.angle(), 0.0) + + def test_zero_mask_outline(self): + """Ensures outline correctly handles zero sized masks.""" + expected_points = [] + + for size in ((61, 0), (0, 60), (0, 0)): + mask = pygame.mask.Mask(size) + + points = mask.outline() + + self.assertListEqual(points, expected_points, "size={}".format(size)) + + def test_zero_mask_outline__with_arg(self): + """Ensures outline correctly handles zero sized masks + when using the skip pixels argument.""" + expected_points = [] + + for size in ((66, 0), (0, 65), (0, 0)): + mask = pygame.mask.Mask(size) + + points = mask.outline(10) + + self.assertListEqual(points, expected_points, "size={}".format(size)) + + def test_zero_mask_convolve(self): + """Ensures convolve correctly handles zero sized masks. + + Tests the different combinations of sized and zero sized masks. + """ + for size1 in ((17, 13), (71, 0), (0, 70), (0, 0)): + mask1 = pygame.mask.Mask(size1, fill=True) + + for size2 in ((11, 7), (81, 0), (0, 60), (0, 0)): + msg = "sizes={}, {}".format(size1, size2) + mask2 = pygame.mask.Mask(size2, fill=True) + expected_size = ( + max(0, size1[0] + size2[0] - 1), + max(0, size1[1] + size2[1] - 1), + ) + + mask = mask1.convolve(mask2) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertIsNot(mask, mask2, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + + def test_zero_mask_convolve__with_output_mask(self): + """Ensures convolve correctly handles zero sized masks + when using an output mask argument. + + Tests the different combinations of sized and zero sized masks. + """ + for size1 in ((11, 17), (91, 0), (0, 90), (0, 0)): + mask1 = pygame.mask.Mask(size1, fill=True) + + for size2 in ((13, 11), (83, 0), (0, 62), (0, 0)): + mask2 = pygame.mask.Mask(size2, fill=True) + + for output_size in ((7, 5), (71, 0), (0, 70), (0, 0)): + msg = "sizes={}, {}, {}".format(size1, size2, output_size) + output_mask = pygame.mask.Mask(output_size) + + mask = mask1.convolve(mask2, output_mask) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertIs(mask, output_mask, msg) + self.assertEqual(mask.get_size(), output_size, msg) + + def test_zero_mask_connected_component(self): + """Ensures connected_component correctly handles zero sized masks.""" + expected_count = 0 + + for size in ((81, 0), (0, 80), (0, 0)): + msg = "size={}".format(size) + mask = pygame.mask.Mask(size) + + cc_mask = mask.connected_component() + + self.assertIsInstance(cc_mask, pygame.mask.Mask, msg) + self.assertEqual(cc_mask.get_size(), size) + self.assertEqual(cc_mask.count(), expected_count, msg) + + def test_zero_mask_connected_component__indexed(self): + """Ensures connected_component correctly handles zero sized masks + when using an index argument.""" + for size in ((91, 0), (0, 90), (0, 0)): + mask = pygame.mask.Mask(size) + + with self.assertRaises(IndexError): + cc_mask = mask.connected_component((0, 0)) + + def test_zero_mask_connected_components(self): + """Ensures connected_components correctly handles zero sized masks.""" + expected_cc_masks = [] + + for size in ((11, 0), (0, 10), (0, 0)): + mask = pygame.mask.Mask(size) + + cc_masks = mask.connected_components() + + self.assertListEqual(cc_masks, expected_cc_masks, "size={}".format(size)) + + def test_zero_mask_get_bounding_rects(self): + """Ensures get_bounding_rects correctly handles zero sized masks.""" + expected_bounding_rects = [] + + for size in ((21, 0), (0, 20), (0, 0)): + mask = pygame.mask.Mask(size) + + bounding_rects = mask.get_bounding_rects() + + self.assertListEqual( + bounding_rects, expected_bounding_rects, "size={}".format(size) + ) + + def test_zero_mask_to_surface(self): + """Ensures to_surface correctly handles zero sized masks and surfaces. + """ + mask_color = pygame.Color("blue") + surf_color = pygame.Color("red") + + for surf_size in ((7, 3), (7, 0), (0, 7), (0, 0)): + surface = pygame.Surface(surf_size, SRCALPHA, 32) + surface.fill(surf_color) + + for mask_size in ((5, 0), (0, 5), (0, 0)): + mask = pygame.mask.Mask(mask_size, fill=True) + + to_surface = mask.to_surface(surface, setcolor=mask_color) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), surf_size) + + if 0 not in surf_size: + assertSurfaceFilled(self, to_surface, surf_color) + + def test_zero_mask_to_surface__create_surface(self): + """Ensures to_surface correctly handles zero sized masks and surfaces + when it has to create a default surface. + """ + mask_color = pygame.Color("blue") + + for mask_size in ((3, 0), (0, 3), (0, 0)): + mask = pygame.mask.Mask(mask_size, fill=True) + + to_surface = mask.to_surface(setcolor=mask_color) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + +class SubMask(pygame.mask.Mask): + """Subclass of the Mask class to help test subclassing.""" + + def __init__(self, *args, **kwargs): + super(SubMask, self).__init__(*args, **kwargs) + self.test_attribute = True + + +class SubMaskCopy(SubMask): + """Subclass of the Mask class to help test copying subclasses.""" + + def copy(self): + mask_copy = super(SubMaskCopy, self).copy() + mask_copy.test_attribute = self.test_attribute + return mask_copy + + +class SubMaskDunderCopy(SubMask): + """Subclass of the Mask class to help test copying subclasses.""" + + def __copy__(self): + mask_copy = super(SubMaskDunderCopy, self).__copy__() + mask_copy.test_attribute = self.test_attribute + return mask_copy + + +class SubMaskCopyAndDunderCopy(SubMaskDunderCopy): + """Subclass of the Mask class to help test copying subclasses.""" + + def copy(self): + return super(SubMaskCopyAndDunderCopy, self).copy() + + +class MaskSubclassTest(unittest.TestCase): + """Test subclassed Masks.""" + + def test_subclass_mask(self): + """Ensures the Mask class can be subclassed.""" + mask = SubMask((5, 3), fill=True) + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertIsInstance(mask, SubMask) + self.assertTrue(mask.test_attribute) + + def test_subclass_copy(self): + """Ensures copy works for subclassed Masks.""" + mask = SubMask((65, 2), fill=True) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsInstance(mask_copy, SubMask) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + # No subclass attributes because copy()/__copy__() not overridden. + self.assertFalse(hasattr(mask_copy, "test_attribute")) + + def test_subclass_copy__override_copy(self): + """Ensures copy works for subclassed Masks overriding copy.""" + mask = SubMaskCopy((65, 2), fill=True) + + # Test both the copy() and __copy__() methods. + for i, mask_copy in enumerate((mask.copy(), copy.copy(mask))): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsInstance(mask_copy, SubMaskCopy) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + + if 1 == i: + # No subclass attributes because __copy__() not overridden. + self.assertFalse(hasattr(mask_copy, "test_attribute")) + else: + self.assertTrue(mask_copy.test_attribute) + + def test_subclass_copy__override_dunder_copy(self): + """Ensures copy works for subclassed Masks overriding __copy__.""" + mask = SubMaskDunderCopy((65, 2), fill=True) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsInstance(mask_copy, SubMaskDunderCopy) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + # Calls to copy() eventually call __copy__() internally so the + # attributes will be copied. + self.assertTrue(mask_copy.test_attribute) + + def test_subclass_copy__override_both_copy_methods(self): + """Ensures copy works for subclassed Masks overriding copy/__copy__.""" + mask = SubMaskCopyAndDunderCopy((65, 2), fill=True) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsInstance(mask_copy, SubMaskCopyAndDunderCopy) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + self.assertTrue(mask_copy.test_attribute) + + def test_subclass_get_size(self): + """Ensures get_size works for subclassed Masks.""" + expected_size = (2, 3) + mask = SubMask(expected_size) + + size = mask.get_size() + + self.assertEqual(size, expected_size) + + def test_subclass_mask_get_rect(self): + """Ensures get_rect works for subclassed Masks.""" + expected_rect = pygame.Rect((0, 0), (65, 33)) + mask = SubMask(expected_rect.size, fill=True) + + rect = mask.get_rect() + + self.assertEqual(rect, expected_rect) + + def test_subclass_get_at(self): + """Ensures get_at works for subclassed Masks.""" + expected_bit = 1 + mask = SubMask((3, 2), fill=True) + + bit = mask.get_at((0, 0)) + + self.assertEqual(bit, expected_bit) + + def test_subclass_set_at(self): + """Ensures set_at works for subclassed Masks.""" + expected_bit = 1 + expected_count = 1 + pos = (0, 0) + mask = SubMask(fill=False, size=(4, 2)) + + mask.set_at(pos) + + self.assertEqual(mask.get_at(pos), expected_bit) + self.assertEqual(mask.count(), expected_count) + + def test_subclass_overlap(self): + """Ensures overlap works for subclassed Masks.""" + expected_pos = (0, 0) + mask_size = (2, 3) + masks = (pygame.mask.Mask(fill=True, size=mask_size), SubMask(mask_size, True)) + arg_masks = ( + pygame.mask.Mask(fill=True, size=mask_size), + SubMask(mask_size, True), + ) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in masks: + for arg_mask in arg_masks: + overlap_pos = mask.overlap(arg_mask, (0, 0)) + + self.assertEqual(overlap_pos, expected_pos) + + def test_subclass_overlap_area(self): + """Ensures overlap_area works for subclassed Masks.""" + mask_size = (3, 2) + expected_count = mask_size[0] * mask_size[1] + masks = (pygame.mask.Mask(fill=True, size=mask_size), SubMask(mask_size, True)) + arg_masks = ( + pygame.mask.Mask(fill=True, size=mask_size), + SubMask(mask_size, True), + ) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in masks: + for arg_mask in arg_masks: + overlap_count = mask.overlap_area(arg_mask, (0, 0)) + + self.assertEqual(overlap_count, expected_count) + + def test_subclass_overlap_mask(self): + """Ensures overlap_mask works for subclassed Masks.""" + expected_size = (4, 5) + expected_count = expected_size[0] * expected_size[1] + masks = ( + pygame.mask.Mask(fill=True, size=expected_size), + SubMask(expected_size, True), + ) + arg_masks = ( + pygame.mask.Mask(fill=True, size=expected_size), + SubMask(expected_size, True), + ) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in masks: + for arg_mask in arg_masks: + overlap_mask = mask.overlap_mask(arg_mask, (0, 0)) + + self.assertIsInstance(overlap_mask, pygame.mask.Mask) + self.assertNotIsInstance(overlap_mask, SubMask) + self.assertEqual(overlap_mask.count(), expected_count) + self.assertEqual(overlap_mask.get_size(), expected_size) + + def test_subclass_fill(self): + """Ensures fill works for subclassed Masks.""" + mask_size = (2, 4) + expected_count = mask_size[0] * mask_size[1] + mask = SubMask(fill=False, size=mask_size) + + mask.fill() + + self.assertEqual(mask.count(), expected_count) + + def test_subclass_clear(self): + """Ensures clear works for subclassed Masks.""" + mask_size = (4, 3) + expected_count = 0 + mask = SubMask(mask_size, True) + + mask.clear() + + self.assertEqual(mask.count(), expected_count) + + def test_subclass_invert(self): + """Ensures invert works for subclassed Masks.""" + mask_size = (1, 4) + expected_count = mask_size[0] * mask_size[1] + mask = SubMask(fill=False, size=mask_size) + + mask.invert() + + self.assertEqual(mask.count(), expected_count) + + def test_subclass_scale(self): + """Ensures scale works for subclassed Masks.""" + expected_size = (5, 2) + mask = SubMask((1, 4)) + + scaled_mask = mask.scale(expected_size) + + self.assertIsInstance(scaled_mask, pygame.mask.Mask) + self.assertNotIsInstance(scaled_mask, SubMask) + self.assertEqual(scaled_mask.get_size(), expected_size) + + def test_subclass_draw(self): + """Ensures draw works for subclassed Masks.""" + mask_size = (5, 4) + expected_count = mask_size[0] * mask_size[1] + arg_masks = ( + pygame.mask.Mask(fill=True, size=mask_size), + SubMask(mask_size, True), + ) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in (pygame.mask.Mask(mask_size), SubMask(mask_size)): + for arg_mask in arg_masks: + mask.clear() # Clear for each test. + + mask.draw(arg_mask, (0, 0)) + + self.assertEqual(mask.count(), expected_count) + + def test_subclass_erase(self): + """Ensures erase works for subclassed Masks.""" + mask_size = (3, 4) + expected_count = 0 + masks = (pygame.mask.Mask(mask_size, True), SubMask(mask_size, True)) + arg_masks = (pygame.mask.Mask(mask_size, True), SubMask(mask_size, True)) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in masks: + for arg_mask in arg_masks: + mask.fill() # Fill for each test. + + mask.erase(arg_mask, (0, 0)) + + self.assertEqual(mask.count(), expected_count) + + def test_subclass_count(self): + """Ensures count works for subclassed Masks.""" + mask_size = (5, 2) + expected_count = mask_size[0] * mask_size[1] - 1 + mask = SubMask(fill=True, size=mask_size) + mask.set_at((1, 1), 0) + + count = mask.count() + + self.assertEqual(count, expected_count) + + def test_subclass_centroid(self): + """Ensures centroid works for subclassed Masks.""" + expected_centroid = (0, 0) + mask_size = (3, 2) + mask = SubMask((3, 2)) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_subclass_angle(self): + """Ensures angle works for subclassed Masks.""" + expected_angle = 0.0 + mask = SubMask(size=(5, 4)) + + angle = mask.angle() + + self.assertAlmostEqual(angle, expected_angle) + + def test_subclass_outline(self): + """Ensures outline works for subclassed Masks.""" + expected_outline = [] + mask = SubMask((3, 4)) + + outline = mask.outline() + + self.assertListEqual(outline, expected_outline) + + def test_subclass_convolve(self): + """Ensures convolve works for subclassed Masks.""" + width, height = 7, 5 + mask_size = (width, height) + expected_count = 0 + expected_size = (max(0, width * 2 - 1), max(0, height * 2 - 1)) + + arg_masks = (pygame.mask.Mask(mask_size), SubMask(mask_size)) + output_masks = (pygame.mask.Mask(mask_size), SubMask(mask_size)) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in (pygame.mask.Mask(mask_size), SubMask(mask_size)): + for arg_mask in arg_masks: + convolve_mask = mask.convolve(arg_mask) + + self.assertIsInstance(convolve_mask, pygame.mask.Mask) + self.assertNotIsInstance(convolve_mask, SubMask) + self.assertEqual(convolve_mask.count(), expected_count) + self.assertEqual(convolve_mask.get_size(), expected_size) + + # Test subclassed masks for the output_mask as well. + for output_mask in output_masks: + convolve_mask = mask.convolve(arg_mask, output_mask) + + self.assertIsInstance(convolve_mask, pygame.mask.Mask) + self.assertEqual(convolve_mask.count(), expected_count) + self.assertEqual(convolve_mask.get_size(), mask_size) + + if isinstance(output_mask, SubMask): + self.assertIsInstance(convolve_mask, SubMask) + else: + self.assertNotIsInstance(convolve_mask, SubMask) + + def test_subclass_connected_component(self): + """Ensures connected_component works for subclassed Masks.""" + expected_count = 0 + expected_size = (3, 4) + mask = SubMask(expected_size) + + cc_mask = mask.connected_component() + + self.assertIsInstance(cc_mask, pygame.mask.Mask) + self.assertNotIsInstance(cc_mask, SubMask) + self.assertEqual(cc_mask.count(), expected_count) + self.assertEqual(cc_mask.get_size(), expected_size) + + def test_subclass_connected_components(self): + """Ensures connected_components works for subclassed Masks.""" + expected_ccs = [] + mask = SubMask((5, 4)) + + ccs = mask.connected_components() + + self.assertListEqual(ccs, expected_ccs) + + def test_subclass_get_bounding_rects(self): + """Ensures get_bounding_rects works for subclassed Masks.""" + expected_bounding_rects = [] + mask = SubMask((3, 2)) + + bounding_rects = mask.get_bounding_rects() + + self.assertListEqual(bounding_rects, expected_bounding_rects) + + def test_subclass_to_surface(self): + """Ensures to_surface works for subclassed Masks.""" + expected_color = pygame.Color("blue") + size = (5, 3) + mask = SubMask(size, fill=True) + surface = pygame.Surface(size, SRCALPHA, 32) + surface.fill(pygame.Color("red")) + + to_surface = mask.to_surface(surface, setcolor=expected_color) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + +class MaskModuleTest(unittest.TestCase): + def test_from_surface(self): + """Ensures from_surface creates a mask with the correct bits set. + + This test checks the masks created by the from_surface function using + 16 and 32 bit surfaces. Each alpha value (0-255) is tested against + several different threshold values. + Note: On 16 bit surface the requested alpha value can differ from what + is actually set. This test uses the value read from the surface. + """ + threshold_count = 256 + surface_color = [55, 155, 255, 0] + expected_size = (11, 9) + all_set_count = expected_size[0] * expected_size[1] + none_set_count = 0 + + for depth in (16, 32): + surface = pygame.Surface(expected_size, SRCALPHA, depth) + + for alpha in range(threshold_count): + surface_color[3] = alpha + surface.fill(surface_color) + + if depth < 32: + # On surfaces with depths < 32 the requested alpha can be + # different than what gets set. Use the value read from the + # surface. + alpha = surface.get_at((0, 0))[3] + + # Test the mask created at threshold values low, high and + # around alpha. + threshold_test_values = set( + [-1, 0, alpha - 1, alpha, alpha + 1, 255, 256] + ) + + for threshold in threshold_test_values: + msg = "depth={}, alpha={}, threshold={}".format( + depth, alpha, threshold + ) + + if alpha > threshold: + expected_count = all_set_count + else: + expected_count = none_set_count + + mask = pygame.mask.from_surface(surface, threshold) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + self.assertEqual(mask.count(), expected_count, msg) + + def test_from_surface__different_alphas_32bit(self): + """Ensures from_surface creates a mask with the correct bits set + when pixels have different alpha values (32 bits surfaces). + + This test checks the masks created by the from_surface function using + a 32 bit surface. The surface is created with each pixel having a + different alpha value (0-255). This surface is tested over a range + of threshold values (0-255). + """ + offset = (0, 0) + threshold_count = 256 + surface_color = [10, 20, 30, 0] + expected_size = (threshold_count, 1) + expected_mask = pygame.Mask(expected_size, fill=True) + surface = pygame.Surface(expected_size, SRCALPHA, 32) + + # Give each pixel a different alpha. + surface.lock() # Lock for possible speed up. + for a in range(threshold_count): + surface_color[3] = a + surface.set_at((a, 0), surface_color) + surface.unlock() + + # Test the mask created for each different alpha threshold. + for threshold in range(threshold_count): + msg = "threshold={}".format(threshold) + expected_mask.set_at((threshold, 0), 0) + expected_count = expected_mask.count() + + mask = pygame.mask.from_surface(surface, threshold) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + self.assertEqual(mask.count(), expected_count, msg) + self.assertEqual( + mask.overlap_area(expected_mask, offset), expected_count, msg + ) + + def test_from_surface__different_alphas_16bit(self): + """Ensures from_surface creates a mask with the correct bits set + when pixels have different alpha values (16 bit surfaces). + + This test checks the masks created by the from_surface function using + a 16 bit surface. Each pixel of the surface is set with a different + alpha value (0-255), but since this is a 16 bit surface the requested + alpha value can differ from what is actually set. The resulting surface + will have groups of alpha values which complicates the test as the + alpha groups will all be set/unset at a given threshold. The setup + calculates these groups and an expected mask for each. This test data + is then used to test each alpha grouping over a range of threshold + values. + """ + threshold_count = 256 + surface_color = [110, 120, 130, 0] + expected_size = (threshold_count, 1) + surface = pygame.Surface(expected_size, SRCALPHA, 16) + + # Give each pixel a different alpha. + surface.lock() # Lock for possible speed up. + for a in range(threshold_count): + surface_color[3] = a + surface.set_at((a, 0), surface_color) + surface.unlock() + + alpha_thresholds = OrderedDict() + special_thresholds = set() + + # Create the threshold ranges and identify any thresholds that need + # special handling. + for threshold in range(threshold_count): + # On surfaces with depths < 32 the requested alpha can be different + # than what gets set. Use the value read from the surface. + alpha = surface.get_at((threshold, 0))[3] + + if alpha not in alpha_thresholds: + alpha_thresholds[alpha] = [threshold] + else: + alpha_thresholds[alpha].append(threshold) + + if threshold < alpha: + special_thresholds.add(threshold) + + # Use each threshold group to create an expected mask. + test_data = [] # [(from_threshold, to_threshold, expected_mask), ...] + offset = (0, 0) + erase_mask = pygame.Mask(expected_size) + exp_mask = pygame.Mask(expected_size, fill=True) + + for thresholds in alpha_thresholds.values(): + for threshold in thresholds: + if threshold in special_thresholds: + # Any special thresholds just reuse previous exp_mask. + test_data.append((threshold, threshold + 1, exp_mask)) + else: + to_threshold = thresholds[-1] + 1 + + # Make the expected mask by erasing the unset bits. + for thres in range(to_threshold): + erase_mask.set_at((thres, 0), 1) + + exp_mask = pygame.Mask(expected_size, fill=True) + exp_mask.erase(erase_mask, offset) + test_data.append((threshold, to_threshold, exp_mask)) + break + + # All the setup is done. Now test the masks created over the threshold + # ranges. + for from_threshold, to_threshold, expected_mask in test_data: + expected_count = expected_mask.count() + + for threshold in range(from_threshold, to_threshold): + msg = "threshold={}".format(threshold) + + mask = pygame.mask.from_surface(surface, threshold) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + self.assertEqual(mask.count(), expected_count, msg) + self.assertEqual( + mask.overlap_area(expected_mask, offset), expected_count, msg + ) + + def test_from_surface__with_colorkey_mask_cleared(self): + """Ensures from_surface creates a mask with the correct bits set + when the surface uses a colorkey. + + The surface is filled with the colorkey color so the resulting masks + are expected to have no bits set. + """ + colorkeys = ((0, 0, 0), (1, 2, 3), (50, 100, 200), (255, 255, 255)) + expected_size = (7, 11) + expected_count = 0 + + for depth in (8, 16, 24, 32): + msg = "depth={}".format(depth) + surface = pygame.Surface(expected_size, 0, depth) + + for colorkey in colorkeys: + surface.set_colorkey(colorkey) + # With some depths (i.e. 8 and 16) the actual colorkey can be + # different than what was requested via the set. + surface.fill(surface.get_colorkey()) + + mask = pygame.mask.from_surface(surface) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + self.assertEqual(mask.count(), expected_count, msg) + + def test_from_surface__with_colorkey_mask_filled(self): + """Ensures from_surface creates a mask with the correct bits set + when the surface uses a colorkey. + + The surface is filled with a color that is not the colorkey color so + the resulting masks are expected to have all bits set. + """ + colorkeys = ((0, 0, 0), (1, 2, 3), (10, 100, 200), (255, 255, 255)) + surface_color = (50, 100, 200) + expected_size = (11, 7) + expected_count = expected_size[0] * expected_size[1] + + for depth in (8, 16, 24, 32): + msg = "depth={}".format(depth) + surface = pygame.Surface(expected_size, 0, depth) + surface.fill(surface_color) + + for colorkey in colorkeys: + surface.set_colorkey(colorkey) + + mask = pygame.mask.from_surface(surface) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + self.assertEqual(mask.count(), expected_count, msg) + + def test_from_surface__with_colorkey_mask_pattern(self): + """Ensures from_surface creates a mask with the correct bits set + when the surface uses a colorkey. + + The surface is filled with alternating pixels of colorkey and + non-colorkey colors, so the resulting masks are expected to have + alternating bits set. + """ + + def alternate(func, set_value, unset_value, width, height): + # Helper function to set alternating values. + setbit = False + for pos in ((x, y) for x in range(width) for y in range(height)): + func(pos, set_value if setbit else unset_value) + setbit = not setbit + + surface_color = (5, 10, 20) + colorkey = (50, 60, 70) + expected_size = (11, 2) + expected_mask = pygame.mask.Mask(expected_size) + alternate(expected_mask.set_at, 1, 0, *expected_size) + expected_count = expected_mask.count() + offset = (0, 0) + + for depth in (8, 16, 24, 32): + msg = "depth={}".format(depth) + surface = pygame.Surface(expected_size, 0, depth) + # Fill the surface with alternating colors. + alternate(surface.set_at, surface_color, colorkey, *expected_size) + surface.set_colorkey(colorkey) + + mask = pygame.mask.from_surface(surface) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + self.assertEqual(mask.count(), expected_count, msg) + self.assertEqual( + mask.overlap_area(expected_mask, offset), expected_count, msg + ) + + def test_from_threshold(self): + """ Does mask.from_threshold() work correctly? + """ + + a = [16, 24, 32] + + for i in a: + surf = pygame.surface.Surface((70, 70), 0, i) + surf.fill((100, 50, 200), (20, 20, 20, 20)) + mask = pygame.mask.from_threshold( + surf, (100, 50, 200, 255), (10, 10, 10, 255) + ) + + rects = mask.get_bounding_rects() + + self.assertEqual(mask.count(), 400) + self.assertEqual(mask.get_bounding_rects(), [pygame.Rect((20, 20, 20, 20))]) + + for i in a: + surf = pygame.surface.Surface((70, 70), 0, i) + surf2 = pygame.surface.Surface((70, 70), 0, i) + surf.fill((100, 100, 100)) + surf2.fill((150, 150, 150)) + surf2.fill((100, 100, 100), (40, 40, 10, 10)) + mask = pygame.mask.from_threshold( + surf, (0, 0, 0, 0), (10, 10, 10, 255), surf2 + ) + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertEqual(mask.count(), 100) + self.assertEqual(mask.get_bounding_rects(), [pygame.Rect((40, 40, 10, 10))]) + + def test_zero_size_from_surface(self): + """Ensures from_surface can create masks from zero sized surfaces.""" + for size in ((100, 0), (0, 100), (0, 0)): + mask = pygame.mask.from_surface(pygame.Surface(size)) + + self.assertIsInstance(mask, pygame.mask.MaskType, "size={}".format(size)) + self.assertEqual(mask.get_size(), size) + + def test_zero_size_from_threshold(self): + a = [16, 24, 32] + sizes = ((100, 0), (0, 100), (0, 0)) + + for size in sizes: + for i in a: + surf = pygame.surface.Surface(size, 0, i) + surf.fill((100, 50, 200), (20, 20, 20, 20)) + mask = pygame.mask.from_threshold( + surf, (100, 50, 200, 255), (10, 10, 10, 255) + ) + + self.assertEqual(mask.count(), 0) + + rects = mask.get_bounding_rects() + self.assertEqual(rects, []) + + for i in a: + surf = pygame.surface.Surface(size, 0, i) + surf2 = pygame.surface.Surface(size, 0, i) + surf.fill((100, 100, 100)) + surf2.fill((150, 150, 150)) + surf2.fill((100, 100, 100), (40, 40, 10, 10)) + mask = pygame.mask.from_threshold( + surf, (0, 0, 0, 0), (10, 10, 10, 255), surf2 + ) + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertEqual(mask.count(), 0) + + rects = mask.get_bounding_rects() + self.assertEqual(rects, []) + + @unittest.skipIf(sys.version_info < (3,), "Python 3 only") + def test_buffer_interface(self): + size = (1000, 100) + pixels_set = ((0, 1), (100, 10), (173, 90)) + pixels_unset = ((0, 0), (101, 10), (173, 91)) + + mask = pygame.Mask(size) + for point in pixels_set: + mask.set_at(point, 1) + + view = memoryview(mask) + intwidth = 8 * view.strides[1] + + for point in pixels_set: + x, y = point + col = x // intwidth + self.assertEqual( + (view[col, y] >> (x % intwidth)) & 1, + 1, + "the pixel at {} is not set to 1".format(point), + ) + + for point in pixels_unset: + x, y = point + col = x // intwidth + self.assertEqual( + (view[col, y] >> (x % intwidth)) & 1, + 0, + "the pixel at {} is not set to 0".format(point), + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/math_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/math_test.py new file mode 100644 index 0000000..ef22246 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/math_test.py @@ -0,0 +1,2116 @@ +# -*- coding: utf-8 -*- +import sys +import unittest +import math +import platform + +import pygame.math +from pygame.math import Vector2, Vector3 + +IS_PYPY = "PyPy" == platform.python_implementation() +PY3 = sys.version_info.major == 3 + + +class Vector2TypeTest(unittest.TestCase): + def setUp(self): + pygame.math.enable_swizzling() + self.zeroVec = Vector2() + self.e1 = Vector2(1, 0) + self.e2 = Vector2(0, 1) + self.t1 = (1.2, 3.4) + self.l1 = list(self.t1) + self.v1 = Vector2(self.t1) + self.t2 = (5.6, 7.8) + self.l2 = list(self.t2) + self.v2 = Vector2(self.t2) + self.s1 = 5.6 + self.s2 = 7.8 + + def tearDown(self): + pygame.math.enable_swizzling() + + def testConstructionDefault(self): + v = Vector2() + self.assertEqual(v.x, 0.0) + self.assertEqual(v.y, 0.0) + + def testConstructionScalar(self): + v = Vector2(1) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 1.0) + + def testConstructionScalarKeywords(self): + v = Vector2(x=1) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 1.0) + + def testConstructionKeywords(self): + v = Vector2(x=1, y=2) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 2.0) + + def testConstructionXY(self): + v = Vector2(1.2, 3.4) + self.assertEqual(v.x, 1.2) + self.assertEqual(v.y, 3.4) + + def testConstructionTuple(self): + v = Vector2((1.2, 3.4)) + self.assertEqual(v.x, 1.2) + self.assertEqual(v.y, 3.4) + + def testConstructionList(self): + v = Vector2([1.2, 3.4]) + self.assertEqual(v.x, 1.2) + self.assertEqual(v.y, 3.4) + + def testConstructionVector2(self): + v = Vector2(Vector2(1.2, 3.4)) + self.assertEqual(v.x, 1.2) + self.assertEqual(v.y, 3.4) + + def testAttributAccess(self): + tmp = self.v1.x + self.assertEqual(tmp, self.v1.x) + self.assertEqual(tmp, self.v1[0]) + tmp = self.v1.y + self.assertEqual(tmp, self.v1.y) + self.assertEqual(tmp, self.v1[1]) + self.v1.x = 3.141 + self.assertEqual(self.v1.x, 3.141) + self.v1.y = 3.141 + self.assertEqual(self.v1.y, 3.141) + + def assign_nonfloat(): + v = Vector2() + v.x = "spam" + + self.assertRaises(TypeError, assign_nonfloat) + + def testSequence(self): + v = Vector2(1.2, 3.4) + Vector2()[:] + self.assertEqual(len(v), 2) + self.assertEqual(v[0], 1.2) + self.assertEqual(v[1], 3.4) + self.assertRaises(IndexError, lambda: v[2]) + self.assertEqual(v[-1], 3.4) + self.assertEqual(v[-2], 1.2) + self.assertRaises(IndexError, lambda: v[-3]) + self.assertEqual(v[:], [1.2, 3.4]) + self.assertEqual(v[1:], [3.4]) + self.assertEqual(v[:1], [1.2]) + self.assertEqual(list(v), [1.2, 3.4]) + self.assertEqual(tuple(v), (1.2, 3.4)) + v[0] = 5.6 + v[1] = 7.8 + self.assertEqual(v.x, 5.6) + self.assertEqual(v.y, 7.8) + v[:] = [9.1, 11.12] + self.assertEqual(v.x, 9.1) + self.assertEqual(v.y, 11.12) + + def overpopulate(): + v = Vector2() + v[:] = [1, 2, 3] + + self.assertRaises(ValueError, overpopulate) + + def underpopulate(): + v = Vector2() + v[:] = [1] + + self.assertRaises(ValueError, underpopulate) + + def assign_nonfloat(): + v = Vector2() + v[0] = "spam" + + self.assertRaises(TypeError, assign_nonfloat) + + def testExtendedSlicing(self): + # deletion + def delSlice(vec, start=None, stop=None, step=None): + if start is not None and stop is not None and step is not None: + del vec[start:stop:step] + elif start is not None and stop is None and step is not None: + del vec[start::step] + elif start is None and stop is None and step is not None: + del vec[::step] + + v = Vector2(self.v1) + self.assertRaises(TypeError, delSlice, v, None, None, 2) + self.assertRaises(TypeError, delSlice, v, 1, None, 2) + self.assertRaises(TypeError, delSlice, v, 1, 2, 1) + + # assignment + v = Vector2(self.v1) + v[::2] = [-1] + self.assertEqual(v, [-1, self.v1.y]) + v = Vector2(self.v1) + v[::-2] = [10] + self.assertEqual(v, [self.v1.x, 10]) + v = Vector2(self.v1) + v[::-1] = v + self.assertEqual(v, [self.v1.y, self.v1.x]) + a = Vector2(self.v1) + b = Vector2(self.v1) + c = Vector2(self.v1) + a[1:2] = [2.2] + b[slice(1, 2)] = [2.2] + c[1:2:] = (2.2,) + self.assertEqual(a, b) + self.assertEqual(a, c) + self.assertEqual(type(a), type(self.v1)) + self.assertEqual(type(b), type(self.v1)) + self.assertEqual(type(c), type(self.v1)) + + def testAdd(self): + v3 = self.v1 + self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x + self.v2.x) + self.assertEqual(v3.y, self.v1.y + self.v2.y) + v3 = self.v1 + self.t2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x + self.t2[0]) + self.assertEqual(v3.y, self.v1.y + self.t2[1]) + v3 = self.v1 + self.l2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x + self.l2[0]) + self.assertEqual(v3.y, self.v1.y + self.l2[1]) + v3 = self.t1 + self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.t1[0] + self.v2.x) + self.assertEqual(v3.y, self.t1[1] + self.v2.y) + v3 = self.l1 + self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.l1[0] + self.v2.x) + self.assertEqual(v3.y, self.l1[1] + self.v2.y) + + def testSub(self): + v3 = self.v1 - self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x - self.v2.x) + self.assertEqual(v3.y, self.v1.y - self.v2.y) + v3 = self.v1 - self.t2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x - self.t2[0]) + self.assertEqual(v3.y, self.v1.y - self.t2[1]) + v3 = self.v1 - self.l2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x - self.l2[0]) + self.assertEqual(v3.y, self.v1.y - self.l2[1]) + v3 = self.t1 - self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.t1[0] - self.v2.x) + self.assertEqual(v3.y, self.t1[1] - self.v2.y) + v3 = self.l1 - self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.l1[0] - self.v2.x) + self.assertEqual(v3.y, self.l1[1] - self.v2.y) + + def testScalarMultiplication(self): + v = self.s1 * self.v1 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertEqual(v.x, self.s1 * self.v1.x) + self.assertEqual(v.y, self.s1 * self.v1.y) + v = self.v1 * self.s2 + self.assertEqual(v.x, self.v1.x * self.s2) + self.assertEqual(v.y, self.v1.y * self.s2) + + def testScalarDivision(self): + v = self.v1 / self.s1 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertAlmostEqual(v.x, self.v1.x / self.s1) + self.assertAlmostEqual(v.y, self.v1.y / self.s1) + v = self.v1 // self.s2 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertEqual(v.x, self.v1.x // self.s2) + self.assertEqual(v.y, self.v1.y // self.s2) + + def testBool(self): + self.assertEqual(bool(self.zeroVec), False) + self.assertEqual(bool(self.v1), True) + self.assertTrue(not self.zeroVec) + self.assertTrue(self.v1) + + def testUnary(self): + v = +self.v1 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertEqual(v.x, self.v1.x) + self.assertEqual(v.y, self.v1.y) + self.assertNotEqual(id(v), id(self.v1)) + v = -self.v1 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertEqual(v.x, -self.v1.x) + self.assertEqual(v.y, -self.v1.y) + self.assertNotEqual(id(v), id(self.v1)) + + def testCompare(self): + int_vec = Vector2(3, -2) + flt_vec = Vector2(3.0, -2.0) + zero_vec = Vector2(0, 0) + self.assertEqual(int_vec == flt_vec, True) + self.assertEqual(int_vec != flt_vec, False) + self.assertEqual(int_vec != zero_vec, True) + self.assertEqual(flt_vec == zero_vec, False) + self.assertEqual(int_vec == (3, -2), True) + self.assertEqual(int_vec != (3, -2), False) + self.assertEqual(int_vec != [0, 0], True) + self.assertEqual(int_vec == [0, 0], False) + self.assertEqual(int_vec != 5, True) + self.assertEqual(int_vec == 5, False) + self.assertEqual(int_vec != [3, -2, 0], True) + self.assertEqual(int_vec == [3, -2, 0], False) + + def testStr(self): + v = Vector2(1.2, 3.4) + self.assertEqual(str(v), "[1.2, 3.4]") + + def testRepr(self): + v = Vector2(1.2, 3.4) + self.assertEqual(v.__repr__(), "") + self.assertEqual(v, Vector2(v.__repr__())) + + def testIter(self): + it = self.v1.__iter__() + if PY3: + next_ = it.__next__ + else: + next_ = it.next + self.assertEqual(next_(), self.v1[0]) + self.assertEqual(next_(), self.v1[1]) + self.assertRaises(StopIteration, lambda: next_()) + it1 = self.v1.__iter__() + it2 = self.v1.__iter__() + self.assertNotEqual(id(it1), id(it2)) + self.assertEqual(id(it1), id(it1.__iter__())) + self.assertEqual(list(it1), list(it2)) + self.assertEqual(list(self.v1.__iter__()), self.l1) + idx = 0 + for val in self.v1: + self.assertEqual(val, self.v1[idx]) + idx += 1 + + def test_rotate(self): + v1 = Vector2(1, 0) + v2 = v1.rotate(90) + v3 = v1.rotate(90 + 360) + self.assertEqual(v1.x, 1) + self.assertEqual(v1.y, 0) + self.assertEqual(v2.x, 0) + self.assertEqual(v2.y, 1) + self.assertEqual(v3.x, v2.x) + self.assertEqual(v3.y, v2.y) + v1 = Vector2(-1, -1) + v2 = v1.rotate(-90) + self.assertEqual(v2.x, -1) + self.assertEqual(v2.y, 1) + v2 = v1.rotate(360) + self.assertEqual(v1.x, v2.x) + self.assertEqual(v1.y, v2.y) + v2 = v1.rotate(0) + self.assertEqual(v1.x, v2.x) + self.assertEqual(v1.y, v2.y) + # issue 214 + self.assertEqual(Vector2(0, 1).rotate(359.99999999), Vector2(0, 1)) + + def test_rotate_rad(self): + tests = ( + ((1, 0), math.pi), + ((1, 0), math.pi / 2), + ((1, 0), -math.pi / 2), + ((1, 0), math.pi / 4), + ) + for initialVec, radians in tests: + self.assertEqual( + Vector2(initialVec).rotate_rad(radians), + (math.cos(radians), math.sin(radians)), + ) + + def test_rotate_ip(self): + v = Vector2(1, 0) + self.assertEqual(v.rotate_ip(90), None) + self.assertEqual(v.x, 0) + self.assertEqual(v.y, 1) + v = Vector2(-1, -1) + v.rotate_ip(-90) + self.assertEqual(v.x, -1) + self.assertEqual(v.y, 1) + + def test_rotate_ip_rad(self): + tests = ( + ((1, 0), math.pi), + ((1, 0), math.pi / 2), + ((1, 0), -math.pi / 2), + ((1, 0), math.pi / 4), + ) + for initialVec, radians in tests: + vec = Vector2(initialVec) + vec.rotate_ip_rad(radians) + self.assertEqual(vec, (math.cos(radians), math.sin(radians))) + + def test_normalize(self): + v = self.v1.normalize() + # length is 1 + self.assertAlmostEqual(v.x * v.x + v.y * v.y, 1.0) + # v1 is unchanged + self.assertEqual(self.v1.x, self.l1[0]) + self.assertEqual(self.v1.y, self.l1[1]) + # v2 is parallel to v1 + self.assertAlmostEqual(self.v1.x * v.y - self.v1.y * v.x, 0.0) + self.assertRaises(ValueError, lambda: self.zeroVec.normalize()) + + def test_normalize_ip(self): + v = +self.v1 + # v has length != 1 before normalizing + self.assertNotEqual(v.x * v.x + v.y * v.y, 1.0) + # inplace operations should return None + self.assertEqual(v.normalize_ip(), None) + # length is 1 + self.assertAlmostEqual(v.x * v.x + v.y * v.y, 1.0) + # v2 is parallel to v1 + self.assertAlmostEqual(self.v1.x * v.y - self.v1.y * v.x, 0.0) + self.assertRaises(ValueError, lambda: self.zeroVec.normalize_ip()) + + def test_is_normalized(self): + self.assertEqual(self.v1.is_normalized(), False) + v = self.v1.normalize() + self.assertEqual(v.is_normalized(), True) + self.assertEqual(self.e2.is_normalized(), True) + self.assertEqual(self.zeroVec.is_normalized(), False) + + def test_cross(self): + self.assertEqual( + self.v1.cross(self.v2), self.v1.x * self.v2.y - self.v1.y * self.v2.x + ) + self.assertEqual( + self.v1.cross(self.l2), self.v1.x * self.l2[1] - self.v1.y * self.l2[0] + ) + self.assertEqual( + self.v1.cross(self.t2), self.v1.x * self.t2[1] - self.v1.y * self.t2[0] + ) + self.assertEqual(self.v1.cross(self.v2), -self.v2.cross(self.v1)) + self.assertEqual(self.v1.cross(self.v1), 0) + + def test_dot(self): + self.assertAlmostEqual( + self.v1.dot(self.v2), self.v1.x * self.v2.x + self.v1.y * self.v2.y + ) + self.assertAlmostEqual( + self.v1.dot(self.l2), self.v1.x * self.l2[0] + self.v1.y * self.l2[1] + ) + self.assertAlmostEqual( + self.v1.dot(self.t2), self.v1.x * self.t2[0] + self.v1.y * self.t2[1] + ) + self.assertEqual(self.v1.dot(self.v2), self.v2.dot(self.v1)) + self.assertEqual(self.v1.dot(self.v2), self.v1 * self.v2) + + def test_angle_to(self): + self.assertEqual( + self.v1.rotate(self.v1.angle_to(self.v2)).normalize(), self.v2.normalize() + ) + self.assertEqual(Vector2(1, 1).angle_to((-1, 1)), 90) + self.assertEqual(Vector2(1, 0).angle_to((0, -1)), -90) + self.assertEqual(Vector2(1, 0).angle_to((-1, 1)), 135) + self.assertEqual(abs(Vector2(1, 0).angle_to((-1, 0))), 180) + + def test_scale_to_length(self): + v = Vector2(1, 1) + v.scale_to_length(2.5) + self.assertEqual(v, Vector2(2.5, 2.5) / math.sqrt(2)) + self.assertRaises(ValueError, lambda: self.zeroVec.scale_to_length(1)) + self.assertEqual(v.scale_to_length(0), None) + self.assertEqual(v, self.zeroVec) + + def test_length(self): + self.assertEqual(Vector2(3, 4).length(), 5) + self.assertEqual(Vector2(-3, 4).length(), 5) + self.assertEqual(self.zeroVec.length(), 0) + + def test_length_squared(self): + self.assertEqual(Vector2(3, 4).length_squared(), 25) + self.assertEqual(Vector2(-3, 4).length_squared(), 25) + self.assertEqual(self.zeroVec.length_squared(), 0) + + def test_reflect(self): + v = Vector2(1, -1) + n = Vector2(0, 1) + self.assertEqual(v.reflect(n), Vector2(1, 1)) + self.assertEqual(v.reflect(3 * n), v.reflect(n)) + self.assertEqual(v.reflect(-v), -v) + self.assertRaises(ValueError, lambda: v.reflect(self.zeroVec)) + + def test_reflect_ip(self): + v1 = Vector2(1, -1) + v2 = Vector2(v1) + n = Vector2(0, 1) + self.assertEqual(v2.reflect_ip(n), None) + self.assertEqual(v2, Vector2(1, 1)) + v2 = Vector2(v1) + v2.reflect_ip(3 * n) + self.assertEqual(v2, v1.reflect(n)) + v2 = Vector2(v1) + v2.reflect_ip(-v1) + self.assertEqual(v2, -v1) + self.assertRaises(ValueError, lambda: v2.reflect_ip(Vector2())) + + def test_distance_to(self): + diff = self.v1 - self.v2 + self.assertEqual(self.e1.distance_to(self.e2), math.sqrt(2)) + self.assertAlmostEqual( + self.v1.distance_to(self.v2), math.sqrt(diff.x * diff.x + diff.y * diff.y) + ) + self.assertEqual(self.v1.distance_to(self.v1), 0) + self.assertEqual(self.v1.distance_to(self.v2), self.v2.distance_to(self.v1)) + + def test_distance_squared_to(self): + diff = self.v1 - self.v2 + self.assertEqual(self.e1.distance_squared_to(self.e2), 2) + self.assertAlmostEqual( + self.v1.distance_squared_to(self.v2), diff.x * diff.x + diff.y * diff.y + ) + self.assertEqual(self.v1.distance_squared_to(self.v1), 0) + self.assertEqual( + self.v1.distance_squared_to(self.v2), self.v2.distance_squared_to(self.v1) + ) + + def test_update(self): + v = Vector2(3, 4) + v.update(0) + self.assertEqual(v, Vector2((0, 0))) + v.update(5, 1) + self.assertEqual(v, Vector2(5, 1)) + v.update((4, 1)) + self.assertNotEqual(v, Vector2((5, 1))) + + def test_swizzle(self): + self.assertTrue(hasattr(pygame.math, "enable_swizzling")) + self.assertTrue(hasattr(pygame.math, "disable_swizzling")) + # swizzling not disabled by default + pygame.math.disable_swizzling() + self.assertRaises(AttributeError, lambda: self.v1.yx) + pygame.math.enable_swizzling() + + self.assertEqual(self.v1.yx, (self.v1.y, self.v1.x)) + self.assertEqual( + self.v1.xxyyxy, + (self.v1.x, self.v1.x, self.v1.y, self.v1.y, self.v1.x, self.v1.y), + ) + self.v1.xy = self.t2 + self.assertEqual(self.v1, self.t2) + self.v1.yx = self.t2 + self.assertEqual(self.v1, (self.t2[1], self.t2[0])) + self.assertEqual(type(self.v1), Vector2) + + def invalidSwizzleX(): + Vector2().xx = (1, 2) + + def invalidSwizzleY(): + Vector2().yy = (1, 2) + + self.assertRaises(AttributeError, invalidSwizzleX) + self.assertRaises(AttributeError, invalidSwizzleY) + + def invalidAssignment(): + Vector2().xy = 3 + + self.assertRaises(TypeError, invalidAssignment) + + def unicodeAttribute(): + getattr(Vector2(), "ä") + + self.assertRaises(AttributeError, unicodeAttribute) + + def test_swizzle_return_types(self): + self.assertEqual(type(self.v1.x), float) + self.assertEqual(type(self.v1.xy), Vector2) + self.assertEqual(type(self.v1.xyx), Vector3) + # but we don't have vector4 or above... so tuple. + self.assertEqual(type(self.v1.xyxy), tuple) + self.assertEqual(type(self.v1.xyxyx), tuple) + + def test_elementwise(self): + # behaviour for "elementwise op scalar" + self.assertEqual( + self.v1.elementwise() + self.s1, (self.v1.x + self.s1, self.v1.y + self.s1) + ) + self.assertEqual( + self.v1.elementwise() - self.s1, (self.v1.x - self.s1, self.v1.y - self.s1) + ) + self.assertEqual( + self.v1.elementwise() * self.s2, (self.v1.x * self.s2, self.v1.y * self.s2) + ) + self.assertEqual( + self.v1.elementwise() / self.s2, (self.v1.x / self.s2, self.v1.y / self.s2) + ) + self.assertEqual( + self.v1.elementwise() // self.s1, + (self.v1.x // self.s1, self.v1.y // self.s1), + ) + self.assertEqual( + self.v1.elementwise() ** self.s1, + (self.v1.x ** self.s1, self.v1.y ** self.s1), + ) + self.assertEqual( + self.v1.elementwise() % self.s1, (self.v1.x % self.s1, self.v1.y % self.s1) + ) + self.assertEqual( + self.v1.elementwise() > self.s1, self.v1.x > self.s1 and self.v1.y > self.s1 + ) + self.assertEqual( + self.v1.elementwise() < self.s1, self.v1.x < self.s1 and self.v1.y < self.s1 + ) + self.assertEqual( + self.v1.elementwise() == self.s1, + self.v1.x == self.s1 and self.v1.y == self.s1, + ) + self.assertEqual( + self.v1.elementwise() != self.s1, + self.v1.x != self.s1 and self.v1.y != self.s1, + ) + self.assertEqual( + self.v1.elementwise() >= self.s1, + self.v1.x >= self.s1 and self.v1.y >= self.s1, + ) + self.assertEqual( + self.v1.elementwise() <= self.s1, + self.v1.x <= self.s1 and self.v1.y <= self.s1, + ) + self.assertEqual( + self.v1.elementwise() != self.s1, + self.v1.x != self.s1 and self.v1.y != self.s1, + ) + # behaviour for "scalar op elementwise" + self.assertEqual(5 + self.v1.elementwise(), Vector2(5, 5) + self.v1) + self.assertEqual(3.5 - self.v1.elementwise(), Vector2(3.5, 3.5) - self.v1) + self.assertEqual(7.5 * self.v1.elementwise(), 7.5 * self.v1) + self.assertEqual( + -3.5 / self.v1.elementwise(), (-3.5 / self.v1.x, -3.5 / self.v1.y) + ) + self.assertEqual( + -3.5 // self.v1.elementwise(), (-3.5 // self.v1.x, -3.5 // self.v1.y) + ) + self.assertEqual( + -3.5 ** self.v1.elementwise(), (-3.5 ** self.v1.x, -3.5 ** self.v1.y) + ) + self.assertEqual(3 % self.v1.elementwise(), (3 % self.v1.x, 3 % self.v1.y)) + self.assertEqual(2 < self.v1.elementwise(), 2 < self.v1.x and 2 < self.v1.y) + self.assertEqual(2 > self.v1.elementwise(), 2 > self.v1.x and 2 > self.v1.y) + self.assertEqual(1 == self.v1.elementwise(), 1 == self.v1.x and 1 == self.v1.y) + self.assertEqual(1 != self.v1.elementwise(), 1 != self.v1.x and 1 != self.v1.y) + self.assertEqual(2 <= self.v1.elementwise(), 2 <= self.v1.x and 2 <= self.v1.y) + self.assertEqual( + -7 >= self.v1.elementwise(), -7 >= self.v1.x and -7 >= self.v1.y + ) + self.assertEqual( + -7 != self.v1.elementwise(), -7 != self.v1.x and -7 != self.v1.y + ) + + # behaviour for "elementwise op vector" + self.assertEqual(type(self.v1.elementwise() * self.v2), type(self.v1)) + self.assertEqual(self.v1.elementwise() + self.v2, self.v1 + self.v2) + self.assertEqual(self.v1.elementwise() + self.v2, self.v1 + self.v2) + self.assertEqual(self.v1.elementwise() - self.v2, self.v1 - self.v2) + self.assertEqual( + self.v1.elementwise() * self.v2, + (self.v1.x * self.v2.x, self.v1.y * self.v2.y), + ) + self.assertEqual( + self.v1.elementwise() / self.v2, + (self.v1.x / self.v2.x, self.v1.y / self.v2.y), + ) + self.assertEqual( + self.v1.elementwise() // self.v2, + (self.v1.x // self.v2.x, self.v1.y // self.v2.y), + ) + self.assertEqual( + self.v1.elementwise() ** self.v2, + (self.v1.x ** self.v2.x, self.v1.y ** self.v2.y), + ) + self.assertEqual( + self.v1.elementwise() % self.v2, + (self.v1.x % self.v2.x, self.v1.y % self.v2.y), + ) + self.assertEqual( + self.v1.elementwise() > self.v2, + self.v1.x > self.v2.x and self.v1.y > self.v2.y, + ) + self.assertEqual( + self.v1.elementwise() < self.v2, + self.v1.x < self.v2.x and self.v1.y < self.v2.y, + ) + self.assertEqual( + self.v1.elementwise() >= self.v2, + self.v1.x >= self.v2.x and self.v1.y >= self.v2.y, + ) + self.assertEqual( + self.v1.elementwise() <= self.v2, + self.v1.x <= self.v2.x and self.v1.y <= self.v2.y, + ) + self.assertEqual( + self.v1.elementwise() == self.v2, + self.v1.x == self.v2.x and self.v1.y == self.v2.y, + ) + self.assertEqual( + self.v1.elementwise() != self.v2, + self.v1.x != self.v2.x and self.v1.y != self.v2.y, + ) + # behaviour for "vector op elementwise" + self.assertEqual(self.v2 + self.v1.elementwise(), self.v2 + self.v1) + self.assertEqual(self.v2 - self.v1.elementwise(), self.v2 - self.v1) + self.assertEqual( + self.v2 * self.v1.elementwise(), + (self.v2.x * self.v1.x, self.v2.y * self.v1.y), + ) + self.assertEqual( + self.v2 / self.v1.elementwise(), + (self.v2.x / self.v1.x, self.v2.y / self.v1.y), + ) + self.assertEqual( + self.v2 // self.v1.elementwise(), + (self.v2.x // self.v1.x, self.v2.y // self.v1.y), + ) + self.assertEqual( + self.v2 ** self.v1.elementwise(), + (self.v2.x ** self.v1.x, self.v2.y ** self.v1.y), + ) + self.assertEqual( + self.v2 % self.v1.elementwise(), + (self.v2.x % self.v1.x, self.v2.y % self.v1.y), + ) + self.assertEqual( + self.v2 < self.v1.elementwise(), + self.v2.x < self.v1.x and self.v2.y < self.v1.y, + ) + self.assertEqual( + self.v2 > self.v1.elementwise(), + self.v2.x > self.v1.x and self.v2.y > self.v1.y, + ) + self.assertEqual( + self.v2 <= self.v1.elementwise(), + self.v2.x <= self.v1.x and self.v2.y <= self.v1.y, + ) + self.assertEqual( + self.v2 >= self.v1.elementwise(), + self.v2.x >= self.v1.x and self.v2.y >= self.v1.y, + ) + self.assertEqual( + self.v2 == self.v1.elementwise(), + self.v2.x == self.v1.x and self.v2.y == self.v1.y, + ) + self.assertEqual( + self.v2 != self.v1.elementwise(), + self.v2.x != self.v1.x and self.v2.y != self.v1.y, + ) + + # behaviour for "elementwise op elementwise" + self.assertEqual( + self.v2.elementwise() + self.v1.elementwise(), self.v2 + self.v1 + ) + self.assertEqual( + self.v2.elementwise() - self.v1.elementwise(), self.v2 - self.v1 + ) + self.assertEqual( + self.v2.elementwise() * self.v1.elementwise(), + (self.v2.x * self.v1.x, self.v2.y * self.v1.y), + ) + self.assertEqual( + self.v2.elementwise() / self.v1.elementwise(), + (self.v2.x / self.v1.x, self.v2.y / self.v1.y), + ) + self.assertEqual( + self.v2.elementwise() // self.v1.elementwise(), + (self.v2.x // self.v1.x, self.v2.y // self.v1.y), + ) + self.assertEqual( + self.v2.elementwise() ** self.v1.elementwise(), + (self.v2.x ** self.v1.x, self.v2.y ** self.v1.y), + ) + self.assertEqual( + self.v2.elementwise() % self.v1.elementwise(), + (self.v2.x % self.v1.x, self.v2.y % self.v1.y), + ) + self.assertEqual( + self.v2.elementwise() < self.v1.elementwise(), + self.v2.x < self.v1.x and self.v2.y < self.v1.y, + ) + self.assertEqual( + self.v2.elementwise() > self.v1.elementwise(), + self.v2.x > self.v1.x and self.v2.y > self.v1.y, + ) + self.assertEqual( + self.v2.elementwise() <= self.v1.elementwise(), + self.v2.x <= self.v1.x and self.v2.y <= self.v1.y, + ) + self.assertEqual( + self.v2.elementwise() >= self.v1.elementwise(), + self.v2.x >= self.v1.x and self.v2.y >= self.v1.y, + ) + self.assertEqual( + self.v2.elementwise() == self.v1.elementwise(), + self.v2.x == self.v1.x and self.v2.y == self.v1.y, + ) + self.assertEqual( + self.v2.elementwise() != self.v1.elementwise(), + self.v2.x != self.v1.x and self.v2.y != self.v1.y, + ) + + # other behaviour + self.assertEqual(abs(self.v1.elementwise()), (abs(self.v1.x), abs(self.v1.y))) + self.assertEqual(-self.v1.elementwise(), -self.v1) + self.assertEqual(+self.v1.elementwise(), +self.v1) + self.assertEqual(bool(self.v1.elementwise()), bool(self.v1)) + self.assertEqual(bool(Vector2().elementwise()), bool(Vector2())) + self.assertEqual(self.zeroVec.elementwise() ** 0, (1, 1)) + self.assertRaises(ValueError, lambda: pow(Vector2(-1, 0).elementwise(), 1.2)) + self.assertRaises(ZeroDivisionError, lambda: self.zeroVec.elementwise() ** -1) + + def test_elementwise(self): + v1 = self.v1 + v2 = self.v2 + s1 = self.s1 + s2 = self.s2 + # behaviour for "elementwise op scalar" + self.assertEqual(v1.elementwise() + s1, (v1.x + s1, v1.y + s1)) + self.assertEqual(v1.elementwise() - s1, (v1.x - s1, v1.y - s1)) + self.assertEqual(v1.elementwise() * s2, (v1.x * s2, v1.y * s2)) + self.assertEqual(v1.elementwise() / s2, (v1.x / s2, v1.y / s2)) + self.assertEqual(v1.elementwise() // s1, (v1.x // s1, v1.y // s1)) + self.assertEqual(v1.elementwise() ** s1, (v1.x ** s1, v1.y ** s1)) + self.assertEqual(v1.elementwise() % s1, (v1.x % s1, v1.y % s1)) + self.assertEqual(v1.elementwise() > s1, v1.x > s1 and v1.y > s1) + self.assertEqual(v1.elementwise() < s1, v1.x < s1 and v1.y < s1) + self.assertEqual(v1.elementwise() == s1, v1.x == s1 and v1.y == s1) + self.assertEqual(v1.elementwise() != s1, v1.x != s1 and v1.y != s1) + self.assertEqual(v1.elementwise() >= s1, v1.x >= s1 and v1.y >= s1) + self.assertEqual(v1.elementwise() <= s1, v1.x <= s1 and v1.y <= s1) + self.assertEqual(v1.elementwise() != s1, v1.x != s1 and v1.y != s1) + # behaviour for "scalar op elementwise" + self.assertEqual(s1 + v1.elementwise(), (s1 + v1.x, s1 + v1.y)) + self.assertEqual(s1 - v1.elementwise(), (s1 - v1.x, s1 - v1.y)) + self.assertEqual(s1 * v1.elementwise(), (s1 * v1.x, s1 * v1.y)) + self.assertEqual(s1 / v1.elementwise(), (s1 / v1.x, s1 / v1.y)) + self.assertEqual(s1 // v1.elementwise(), (s1 // v1.x, s1 // v1.y)) + self.assertEqual(s1 ** v1.elementwise(), (s1 ** v1.x, s1 ** v1.y)) + self.assertEqual(s1 % v1.elementwise(), (s1 % v1.x, s1 % v1.y)) + self.assertEqual(s1 < v1.elementwise(), s1 < v1.x and s1 < v1.y) + self.assertEqual(s1 > v1.elementwise(), s1 > v1.x and s1 > v1.y) + self.assertEqual(s1 == v1.elementwise(), s1 == v1.x and s1 == v1.y) + self.assertEqual(s1 != v1.elementwise(), s1 != v1.x and s1 != v1.y) + self.assertEqual(s1 <= v1.elementwise(), s1 <= v1.x and s1 <= v1.y) + self.assertEqual(s1 >= v1.elementwise(), s1 >= v1.x and s1 >= v1.y) + self.assertEqual(s1 != v1.elementwise(), s1 != v1.x and s1 != v1.y) + + # behaviour for "elementwise op vector" + self.assertEqual(type(v1.elementwise() * v2), type(v1)) + self.assertEqual(v1.elementwise() + v2, v1 + v2) + self.assertEqual(v1.elementwise() - v2, v1 - v2) + self.assertEqual(v1.elementwise() * v2, (v1.x * v2.x, v1.y * v2.y)) + self.assertEqual(v1.elementwise() / v2, (v1.x / v2.x, v1.y / v2.y)) + self.assertEqual(v1.elementwise() // v2, (v1.x // v2.x, v1.y // v2.y)) + self.assertEqual(v1.elementwise() ** v2, (v1.x ** v2.x, v1.y ** v2.y)) + self.assertEqual(v1.elementwise() % v2, (v1.x % v2.x, v1.y % v2.y)) + self.assertEqual(v1.elementwise() > v2, v1.x > v2.x and v1.y > v2.y) + self.assertEqual(v1.elementwise() < v2, v1.x < v2.x and v1.y < v2.y) + self.assertEqual(v1.elementwise() >= v2, v1.x >= v2.x and v1.y >= v2.y) + self.assertEqual(v1.elementwise() <= v2, v1.x <= v2.x and v1.y <= v2.y) + self.assertEqual(v1.elementwise() == v2, v1.x == v2.x and v1.y == v2.y) + self.assertEqual(v1.elementwise() != v2, v1.x != v2.x and v1.y != v2.y) + # behaviour for "vector op elementwise" + self.assertEqual(v2 + v1.elementwise(), v2 + v1) + self.assertEqual(v2 - v1.elementwise(), v2 - v1) + self.assertEqual(v2 * v1.elementwise(), (v2.x * v1.x, v2.y * v1.y)) + self.assertEqual(v2 / v1.elementwise(), (v2.x / v1.x, v2.y / v1.y)) + self.assertEqual(v2 // v1.elementwise(), (v2.x // v1.x, v2.y // v1.y)) + self.assertEqual(v2 ** v1.elementwise(), (v2.x ** v1.x, v2.y ** v1.y)) + self.assertEqual(v2 % v1.elementwise(), (v2.x % v1.x, v2.y % v1.y)) + self.assertEqual(v2 < v1.elementwise(), v2.x < v1.x and v2.y < v1.y) + self.assertEqual(v2 > v1.elementwise(), v2.x > v1.x and v2.y > v1.y) + self.assertEqual(v2 <= v1.elementwise(), v2.x <= v1.x and v2.y <= v1.y) + self.assertEqual(v2 >= v1.elementwise(), v2.x >= v1.x and v2.y >= v1.y) + self.assertEqual(v2 == v1.elementwise(), v2.x == v1.x and v2.y == v1.y) + self.assertEqual(v2 != v1.elementwise(), v2.x != v1.x and v2.y != v1.y) + + # behaviour for "elementwise op elementwise" + self.assertEqual(v2.elementwise() + v1.elementwise(), v2 + v1) + self.assertEqual(v2.elementwise() - v1.elementwise(), v2 - v1) + self.assertEqual( + v2.elementwise() * v1.elementwise(), (v2.x * v1.x, v2.y * v1.y) + ) + self.assertEqual( + v2.elementwise() / v1.elementwise(), (v2.x / v1.x, v2.y / v1.y) + ) + self.assertEqual( + v2.elementwise() // v1.elementwise(), (v2.x // v1.x, v2.y // v1.y) + ) + self.assertEqual( + v2.elementwise() ** v1.elementwise(), (v2.x ** v1.x, v2.y ** v1.y) + ) + self.assertEqual( + v2.elementwise() % v1.elementwise(), (v2.x % v1.x, v2.y % v1.y) + ) + self.assertEqual( + v2.elementwise() < v1.elementwise(), v2.x < v1.x and v2.y < v1.y + ) + self.assertEqual( + v2.elementwise() > v1.elementwise(), v2.x > v1.x and v2.y > v1.y + ) + self.assertEqual( + v2.elementwise() <= v1.elementwise(), v2.x <= v1.x and v2.y <= v1.y + ) + self.assertEqual( + v2.elementwise() >= v1.elementwise(), v2.x >= v1.x and v2.y >= v1.y + ) + self.assertEqual( + v2.elementwise() == v1.elementwise(), v2.x == v1.x and v2.y == v1.y + ) + self.assertEqual( + v2.elementwise() != v1.elementwise(), v2.x != v1.x and v2.y != v1.y + ) + + # other behaviour + self.assertEqual(abs(v1.elementwise()), (abs(v1.x), abs(v1.y))) + self.assertEqual(-v1.elementwise(), -v1) + self.assertEqual(+v1.elementwise(), +v1) + self.assertEqual(bool(v1.elementwise()), bool(v1)) + self.assertEqual(bool(Vector2().elementwise()), bool(Vector2())) + self.assertEqual(self.zeroVec.elementwise() ** 0, (1, 1)) + self.assertRaises(ValueError, lambda: pow(Vector2(-1, 0).elementwise(), 1.2)) + self.assertRaises(ZeroDivisionError, lambda: self.zeroVec.elementwise() ** -1) + self.assertRaises(ZeroDivisionError, lambda: self.zeroVec.elementwise() ** -1) + self.assertRaises(ZeroDivisionError, lambda: Vector2(1, 1).elementwise() / 0) + self.assertRaises(ZeroDivisionError, lambda: Vector2(1, 1).elementwise() // 0) + self.assertRaises(ZeroDivisionError, lambda: Vector2(1, 1).elementwise() % 0) + self.assertRaises( + ZeroDivisionError, lambda: Vector2(1, 1).elementwise() / self.zeroVec + ) + self.assertRaises( + ZeroDivisionError, lambda: Vector2(1, 1).elementwise() // self.zeroVec + ) + self.assertRaises( + ZeroDivisionError, lambda: Vector2(1, 1).elementwise() % self.zeroVec + ) + self.assertRaises(ZeroDivisionError, lambda: 2 / self.zeroVec.elementwise()) + self.assertRaises(ZeroDivisionError, lambda: 2 // self.zeroVec.elementwise()) + self.assertRaises(ZeroDivisionError, lambda: 2 % self.zeroVec.elementwise()) + + def test_slerp(self): + self.assertRaises(ValueError, lambda: self.zeroVec.slerp(self.v1, 0.5)) + self.assertRaises(ValueError, lambda: self.v1.slerp(self.zeroVec, 0.5)) + self.assertRaises(ValueError, lambda: self.zeroVec.slerp(self.zeroVec, 0.5)) + v1 = Vector2(1, 0) + v2 = Vector2(0, 1) + steps = 10 + angle_step = v1.angle_to(v2) / steps + for i, u in ((i, v1.slerp(v2, i / float(steps))) for i in range(steps + 1)): + self.assertAlmostEqual(u.length(), 1) + self.assertAlmostEqual(v1.angle_to(u), i * angle_step) + self.assertEqual(u, v2) + + v1 = Vector2(100, 0) + v2 = Vector2(0, 10) + radial_factor = v2.length() / v1.length() + for i, u in ((i, v1.slerp(v2, -i / float(steps))) for i in range(steps + 1)): + self.assertAlmostEqual( + u.length(), + (v2.length() - v1.length()) * (float(i) / steps) + v1.length(), + ) + self.assertEqual(u, v2) + self.assertEqual(v1.slerp(v1, 0.5), v1) + self.assertEqual(v2.slerp(v2, 0.5), v2) + self.assertRaises(ValueError, lambda: v1.slerp(-v1, 0.5)) + + def test_lerp(self): + v1 = Vector2(0, 0) + v2 = Vector2(10, 10) + self.assertEqual(v1.lerp(v2, 0.5), (5, 5)) + self.assertRaises(ValueError, lambda: v1.lerp(v2, 2.5)) + + v1 = Vector2(-10, -5) + v2 = Vector2(10, 10) + self.assertEqual(v1.lerp(v2, 0.5), (0, 2.5)) + + def test_polar(self): + v = Vector2() + v.from_polar(self.v1.as_polar()) + self.assertEqual(self.v1, v) + self.assertEqual(self.e1.as_polar(), (1, 0)) + self.assertEqual(self.e2.as_polar(), (1, 90)) + self.assertEqual((2 * self.e2).as_polar(), (2, 90)) + self.assertRaises(TypeError, lambda: v.from_polar((None, None))) + self.assertRaises(TypeError, lambda: v.from_polar("ab")) + self.assertRaises(TypeError, lambda: v.from_polar((None, 1))) + self.assertRaises(TypeError, lambda: v.from_polar((1, 2, 3))) + self.assertRaises(TypeError, lambda: v.from_polar((1,))) + self.assertRaises(TypeError, lambda: v.from_polar(1, 2)) + v.from_polar((0.5, 90)) + self.assertEqual(v, 0.5 * self.e2) + v.from_polar((1, 0)) + self.assertEqual(v, self.e1) + + def test_subclass_operation(self): + class Vector(pygame.math.Vector2): + pass + + vec = Vector() + + vec_a = Vector(2, 0) + vec_b = Vector(0, 1) + + vec_a + vec_b + vec_a *= 2 + + +class Vector3TypeTest(unittest.TestCase): + def setUp(self): + self.zeroVec = Vector3() + self.e1 = Vector3(1, 0, 0) + self.e2 = Vector3(0, 1, 0) + self.e3 = Vector3(0, 0, 1) + self.t1 = (1.2, 3.4, 9.6) + self.l1 = list(self.t1) + self.v1 = Vector3(self.t1) + self.t2 = (5.6, 7.8, 2.1) + self.l2 = list(self.t2) + self.v2 = Vector3(self.t2) + self.s1 = 5.6 + self.s2 = 7.8 + + def testConstructionDefault(self): + v = Vector3() + self.assertEqual(v.x, 0.0) + self.assertEqual(v.y, 0.0) + self.assertEqual(v.z, 0.0) + + def testConstructionXYZ(self): + v = Vector3(1.2, 3.4, 9.6) + self.assertEqual(v.x, 1.2) + self.assertEqual(v.y, 3.4) + self.assertEqual(v.z, 9.6) + + def testConstructionTuple(self): + v = Vector3((1.2, 3.4, 9.6)) + self.assertEqual(v.x, 1.2) + self.assertEqual(v.y, 3.4) + self.assertEqual(v.z, 9.6) + + def testConstructionList(self): + v = Vector3([1.2, 3.4, -9.6]) + self.assertEqual(v.x, 1.2) + self.assertEqual(v.y, 3.4) + self.assertEqual(v.z, -9.6) + + def testConstructionVector3(self): + v = Vector3(Vector3(1.2, 3.4, -9.6)) + self.assertEqual(v.x, 1.2) + self.assertEqual(v.y, 3.4) + self.assertEqual(v.z, -9.6) + + def testConstructionScalar(self): + v = Vector3(1) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 1.0) + self.assertEqual(v.z, 1.0) + + def testConstructionScalarKeywords(self): + v = Vector3(x=1) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 1.0) + self.assertEqual(v.z, 1.0) + + def testConstructionKeywords(self): + v = Vector3(x=1, y=2, z=3) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 2.0) + self.assertEqual(v.z, 3.0) + + def testConstructionMissing(self): + def assign_missing_value(): + v = Vector3(1, 2) + + self.assertRaises(ValueError, assign_missing_value) + + def assign_missing_value(): + v = Vector3(x=1, y=2) + + self.assertRaises(ValueError, assign_missing_value) + + def testAttributAccess(self): + tmp = self.v1.x + self.assertEqual(tmp, self.v1.x) + self.assertEqual(tmp, self.v1[0]) + tmp = self.v1.y + self.assertEqual(tmp, self.v1.y) + self.assertEqual(tmp, self.v1[1]) + tmp = self.v1.z + self.assertEqual(tmp, self.v1.z) + self.assertEqual(tmp, self.v1[2]) + self.v1.x = 3.141 + self.assertEqual(self.v1.x, 3.141) + self.v1.y = 3.141 + self.assertEqual(self.v1.y, 3.141) + self.v1.z = 3.141 + self.assertEqual(self.v1.z, 3.141) + + def assign_nonfloat(): + v = Vector2() + v.x = "spam" + + self.assertRaises(TypeError, assign_nonfloat) + + def testSequence(self): + v = Vector3(1.2, 3.4, -9.6) + self.assertEqual(len(v), 3) + self.assertEqual(v[0], 1.2) + self.assertEqual(v[1], 3.4) + self.assertEqual(v[2], -9.6) + self.assertRaises(IndexError, lambda: v[3]) + self.assertEqual(v[-1], -9.6) + self.assertEqual(v[-2], 3.4) + self.assertEqual(v[-3], 1.2) + self.assertRaises(IndexError, lambda: v[-4]) + self.assertEqual(v[:], [1.2, 3.4, -9.6]) + self.assertEqual(v[1:], [3.4, -9.6]) + self.assertEqual(v[:1], [1.2]) + self.assertEqual(v[:-1], [1.2, 3.4]) + self.assertEqual(v[1:2], [3.4]) + self.assertEqual(list(v), [1.2, 3.4, -9.6]) + self.assertEqual(tuple(v), (1.2, 3.4, -9.6)) + v[0] = 5.6 + v[1] = 7.8 + v[2] = -2.1 + self.assertEqual(v.x, 5.6) + self.assertEqual(v.y, 7.8) + self.assertEqual(v.z, -2.1) + v[:] = [9.1, 11.12, -13.41] + self.assertEqual(v.x, 9.1) + self.assertEqual(v.y, 11.12) + self.assertEqual(v.z, -13.41) + + def overpopulate(): + v = Vector3() + v[:] = [1, 2, 3, 4] + + self.assertRaises(ValueError, overpopulate) + + def underpopulate(): + v = Vector3() + v[:] = [1] + + self.assertRaises(ValueError, underpopulate) + + def assign_nonfloat(): + v = Vector2() + v[0] = "spam" + + self.assertRaises(TypeError, assign_nonfloat) + + def testExtendedSlicing(self): + # deletion + def delSlice(vec, start=None, stop=None, step=None): + if start is not None and stop is not None and step is not None: + del vec[start:stop:step] + elif start is not None and stop is None and step is not None: + del vec[start::step] + elif start is None and stop is None and step is not None: + del vec[::step] + + v = Vector3(self.v1) + self.assertRaises(TypeError, delSlice, v, None, None, 2) + self.assertRaises(TypeError, delSlice, v, 1, None, 2) + self.assertRaises(TypeError, delSlice, v, 1, 2, 1) + + # assignment + v = Vector3(self.v1) + v[::2] = [-1.1, -2.2] + self.assertEqual(v, [-1.1, self.v1.y, -2.2]) + v = Vector3(self.v1) + v[::-2] = [10, 20] + self.assertEqual(v, [20, self.v1.y, 10]) + v = Vector3(self.v1) + v[::-1] = v + self.assertEqual(v, [self.v1.z, self.v1.y, self.v1.x]) + a = Vector3(self.v1) + b = Vector3(self.v1) + c = Vector3(self.v1) + a[1:2] = [2.2] + b[slice(1, 2)] = [2.2] + c[1:2:] = (2.2,) + self.assertEqual(a, b) + self.assertEqual(a, c) + self.assertEqual(type(a), type(self.v1)) + self.assertEqual(type(b), type(self.v1)) + self.assertEqual(type(c), type(self.v1)) + + def testAdd(self): + v3 = self.v1 + self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x + self.v2.x) + self.assertEqual(v3.y, self.v1.y + self.v2.y) + self.assertEqual(v3.z, self.v1.z + self.v2.z) + v3 = self.v1 + self.t2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x + self.t2[0]) + self.assertEqual(v3.y, self.v1.y + self.t2[1]) + self.assertEqual(v3.z, self.v1.z + self.t2[2]) + v3 = self.v1 + self.l2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x + self.l2[0]) + self.assertEqual(v3.y, self.v1.y + self.l2[1]) + self.assertEqual(v3.z, self.v1.z + self.l2[2]) + v3 = self.t1 + self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.t1[0] + self.v2.x) + self.assertEqual(v3.y, self.t1[1] + self.v2.y) + self.assertEqual(v3.z, self.t1[2] + self.v2.z) + v3 = self.l1 + self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.l1[0] + self.v2.x) + self.assertEqual(v3.y, self.l1[1] + self.v2.y) + self.assertEqual(v3.z, self.l1[2] + self.v2.z) + + def testSub(self): + v3 = self.v1 - self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x - self.v2.x) + self.assertEqual(v3.y, self.v1.y - self.v2.y) + self.assertEqual(v3.z, self.v1.z - self.v2.z) + v3 = self.v1 - self.t2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x - self.t2[0]) + self.assertEqual(v3.y, self.v1.y - self.t2[1]) + self.assertEqual(v3.z, self.v1.z - self.t2[2]) + v3 = self.v1 - self.l2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x - self.l2[0]) + self.assertEqual(v3.y, self.v1.y - self.l2[1]) + self.assertEqual(v3.z, self.v1.z - self.l2[2]) + v3 = self.t1 - self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.t1[0] - self.v2.x) + self.assertEqual(v3.y, self.t1[1] - self.v2.y) + self.assertEqual(v3.z, self.t1[2] - self.v2.z) + v3 = self.l1 - self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.l1[0] - self.v2.x) + self.assertEqual(v3.y, self.l1[1] - self.v2.y) + self.assertEqual(v3.z, self.l1[2] - self.v2.z) + + def testScalarMultiplication(self): + v = self.s1 * self.v1 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertEqual(v.x, self.s1 * self.v1.x) + self.assertEqual(v.y, self.s1 * self.v1.y) + self.assertEqual(v.z, self.s1 * self.v1.z) + v = self.v1 * self.s2 + self.assertEqual(v.x, self.v1.x * self.s2) + self.assertEqual(v.y, self.v1.y * self.s2) + self.assertEqual(v.z, self.v1.z * self.s2) + + def testScalarDivision(self): + v = self.v1 / self.s1 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertAlmostEqual(v.x, self.v1.x / self.s1) + self.assertAlmostEqual(v.y, self.v1.y / self.s1) + self.assertAlmostEqual(v.z, self.v1.z / self.s1) + v = self.v1 // self.s2 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertEqual(v.x, self.v1.x // self.s2) + self.assertEqual(v.y, self.v1.y // self.s2) + self.assertEqual(v.z, self.v1.z // self.s2) + + def testBool(self): + self.assertEqual(bool(self.zeroVec), False) + self.assertEqual(bool(self.v1), True) + self.assertTrue(not self.zeroVec) + self.assertTrue(self.v1) + + def testUnary(self): + v = +self.v1 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertEqual(v.x, self.v1.x) + self.assertEqual(v.y, self.v1.y) + self.assertEqual(v.z, self.v1.z) + self.assertNotEqual(id(v), id(self.v1)) + v = -self.v1 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertEqual(v.x, -self.v1.x) + self.assertEqual(v.y, -self.v1.y) + self.assertEqual(v.z, -self.v1.z) + self.assertNotEqual(id(v), id(self.v1)) + + def testCompare(self): + int_vec = Vector3(3, -2, 13) + flt_vec = Vector3(3.0, -2.0, 13.0) + zero_vec = Vector3(0, 0, 0) + self.assertEqual(int_vec == flt_vec, True) + self.assertEqual(int_vec != flt_vec, False) + self.assertEqual(int_vec != zero_vec, True) + self.assertEqual(flt_vec == zero_vec, False) + self.assertEqual(int_vec == (3, -2, 13), True) + self.assertEqual(int_vec != (3, -2, 13), False) + self.assertEqual(int_vec != [0, 0], True) + self.assertEqual(int_vec == [0, 0], False) + self.assertEqual(int_vec != 5, True) + self.assertEqual(int_vec == 5, False) + self.assertEqual(int_vec != [3, -2, 0, 1], True) + self.assertEqual(int_vec == [3, -2, 0, 1], False) + + def testStr(self): + v = Vector3(1.2, 3.4, 5.6) + self.assertEqual(str(v), "[1.2, 3.4, 5.6]") + + def testRepr(self): + v = Vector3(1.2, 3.4, -9.6) + self.assertEqual(v.__repr__(), "") + self.assertEqual(v, Vector3(v.__repr__())) + + def testIter(self): + it = self.v1.__iter__() + if PY3: + next_ = it.__next__ + else: + next_ = it.next + self.assertEqual(next_(), self.v1[0]) + self.assertEqual(next_(), self.v1[1]) + self.assertEqual(next_(), self.v1[2]) + self.assertRaises(StopIteration, lambda: next_()) + it1 = self.v1.__iter__() + it2 = self.v1.__iter__() + self.assertNotEqual(id(it1), id(it2)) + self.assertEqual(id(it1), id(it1.__iter__())) + self.assertEqual(list(it1), list(it2)) + self.assertEqual(list(self.v1.__iter__()), self.l1) + idx = 0 + for val in self.v1: + self.assertEqual(val, self.v1[idx]) + idx += 1 + + def test_rotate(self): + v1 = Vector3(1, 0, 0) + axis = Vector3(0, 1, 0) + v2 = v1.rotate(90, axis) + v3 = v1.rotate(90 + 360, axis) + self.assertEqual(v1.x, 1) + self.assertEqual(v1.y, 0) + self.assertEqual(v1.z, 0) + self.assertEqual(v2.x, 0) + self.assertEqual(v2.y, 0) + self.assertEqual(v2.z, -1) + self.assertEqual(v3.x, v2.x) + self.assertEqual(v3.y, v2.y) + self.assertEqual(v3.z, v2.z) + v1 = Vector3(-1, -1, -1) + v2 = v1.rotate(-90, axis) + self.assertEqual(v2.x, 1) + self.assertEqual(v2.y, -1) + self.assertEqual(v2.z, -1) + v2 = v1.rotate(360, axis) + self.assertEqual(v1.x, v2.x) + self.assertEqual(v1.y, v2.y) + self.assertEqual(v1.z, v2.z) + v2 = v1.rotate(0, axis) + self.assertEqual(v1.x, v2.x) + self.assertEqual(v1.y, v2.y) + self.assertEqual(v1.z, v2.z) + # issue 214 + self.assertEqual( + Vector3(0, 1, 0).rotate(359.9999999, Vector3(0, 0, 1)), Vector3(0, 1, 0) + ) + + def test_rotate_rad(self): + axis = Vector3(0, 0, 1) + tests = ( + ((1, 0, 0), math.pi), + ((1, 0, 0), math.pi / 2), + ((1, 0, 0), -math.pi / 2), + ((1, 0, 0), math.pi / 4), + ) + for initialVec, radians in tests: + vec = Vector3(initialVec).rotate_rad(radians, axis) + self.assertEqual(vec, (math.cos(radians), math.sin(radians), 0)) + + def test_rotate_ip(self): + v = Vector3(1, 0, 0) + axis = Vector3(0, 1, 0) + self.assertEqual(v.rotate_ip(90, axis), None) + self.assertEqual(v.x, 0) + self.assertEqual(v.y, 0) + self.assertEqual(v.z, -1) + v = Vector3(-1, -1, 1) + v.rotate_ip(-90, axis) + self.assertEqual(v.x, -1) + self.assertEqual(v.y, -1) + self.assertEqual(v.z, -1) + + def test_rotate_ip_rad(self): + axis = Vector3(0, 0, 1) + tests = ( + ((1, 0, 0), math.pi), + ((1, 0, 0), math.pi / 2), + ((1, 0, 0), -math.pi / 2), + ((1, 0, 0), math.pi / 4), + ) + for initialVec, radians in tests: + vec = Vector3(initialVec) + vec.rotate_ip_rad(radians, axis) + self.assertEqual(vec, (math.cos(radians), math.sin(radians), 0)) + + def test_rotate_x(self): + v1 = Vector3(1, 0, 0) + v2 = v1.rotate_x(90) + v3 = v1.rotate_x(90 + 360) + self.assertEqual(v1.x, 1) + self.assertEqual(v1.y, 0) + self.assertEqual(v1.z, 0) + self.assertEqual(v2.x, 1) + self.assertEqual(v2.y, 0) + self.assertEqual(v2.z, 0) + self.assertEqual(v3.x, v2.x) + self.assertEqual(v3.y, v2.y) + self.assertEqual(v3.z, v2.z) + v1 = Vector3(-1, -1, -1) + v2 = v1.rotate_x(-90) + self.assertEqual(v2.x, -1) + self.assertAlmostEqual(v2.y, -1) + self.assertAlmostEqual(v2.z, 1) + v2 = v1.rotate_x(360) + self.assertAlmostEqual(v1.x, v2.x) + self.assertAlmostEqual(v1.y, v2.y) + self.assertAlmostEqual(v1.z, v2.z) + v2 = v1.rotate_x(0) + self.assertEqual(v1.x, v2.x) + self.assertAlmostEqual(v1.y, v2.y) + self.assertAlmostEqual(v1.z, v2.z) + + def test_rotate_x_rad(self): + vec = Vector3(0, 1, 0) + result = vec.rotate_x_rad(math.pi / 2) + self.assertEqual(result, (0, 0, 1)) + + def test_rotate_x_ip(self): + v = Vector3(1, 0, 0) + self.assertEqual(v.rotate_x_ip(90), None) + self.assertEqual(v.x, 1) + self.assertEqual(v.y, 0) + self.assertEqual(v.z, 0) + v = Vector3(-1, -1, 1) + v.rotate_x_ip(-90) + self.assertEqual(v.x, -1) + self.assertAlmostEqual(v.y, 1) + self.assertAlmostEqual(v.z, 1) + + def test_rotate_x_ip_rad(self): + vec = Vector3(0, 1, 0) + vec.rotate_x_ip_rad(math.pi / 2) + self.assertEqual(vec, (0, 0, 1)) + + def test_rotate_y(self): + v1 = Vector3(1, 0, 0) + v2 = v1.rotate_y(90) + v3 = v1.rotate_y(90 + 360) + self.assertEqual(v1.x, 1) + self.assertEqual(v1.y, 0) + self.assertEqual(v1.z, 0) + self.assertAlmostEqual(v2.x, 0) + self.assertEqual(v2.y, 0) + self.assertAlmostEqual(v2.z, -1) + self.assertAlmostEqual(v3.x, v2.x) + self.assertEqual(v3.y, v2.y) + self.assertAlmostEqual(v3.z, v2.z) + v1 = Vector3(-1, -1, -1) + v2 = v1.rotate_y(-90) + self.assertAlmostEqual(v2.x, 1) + self.assertEqual(v2.y, -1) + self.assertAlmostEqual(v2.z, -1) + v2 = v1.rotate_y(360) + self.assertAlmostEqual(v1.x, v2.x) + self.assertEqual(v1.y, v2.y) + self.assertAlmostEqual(v1.z, v2.z) + v2 = v1.rotate_y(0) + self.assertEqual(v1.x, v2.x) + self.assertEqual(v1.y, v2.y) + self.assertEqual(v1.z, v2.z) + + def test_rotate_y_rad(self): + vec = Vector3(1, 0, 0) + result = vec.rotate_y_rad(math.pi / 2) + self.assertEqual(result, (0, 0, -1)) + + def test_rotate_y_ip(self): + v = Vector3(1, 0, 0) + self.assertEqual(v.rotate_y_ip(90), None) + self.assertAlmostEqual(v.x, 0) + self.assertEqual(v.y, 0) + self.assertAlmostEqual(v.z, -1) + v = Vector3(-1, -1, 1) + v.rotate_y_ip(-90) + self.assertAlmostEqual(v.x, -1) + self.assertEqual(v.y, -1) + self.assertAlmostEqual(v.z, -1) + + def test_rotate_y_ip_rad(self): + vec = Vector3(1, 0, 0) + vec.rotate_y_ip_rad(math.pi / 2) + self.assertEqual(vec, (0, 0, -1)) + + def test_rotate_z(self): + v1 = Vector3(1, 0, 0) + v2 = v1.rotate_z(90) + v3 = v1.rotate_z(90 + 360) + self.assertEqual(v1.x, 1) + self.assertEqual(v1.y, 0) + self.assertEqual(v1.z, 0) + self.assertAlmostEqual(v2.x, 0) + self.assertAlmostEqual(v2.y, 1) + self.assertEqual(v2.z, 0) + self.assertAlmostEqual(v3.x, v2.x) + self.assertAlmostEqual(v3.y, v2.y) + self.assertEqual(v3.z, v2.z) + v1 = Vector3(-1, -1, -1) + v2 = v1.rotate_z(-90) + self.assertAlmostEqual(v2.x, -1) + self.assertAlmostEqual(v2.y, 1) + self.assertEqual(v2.z, -1) + v2 = v1.rotate_z(360) + self.assertAlmostEqual(v1.x, v2.x) + self.assertAlmostEqual(v1.y, v2.y) + self.assertEqual(v1.z, v2.z) + v2 = v1.rotate_z(0) + self.assertAlmostEqual(v1.x, v2.x) + self.assertAlmostEqual(v1.y, v2.y) + self.assertEqual(v1.z, v2.z) + + def test_rotate_z_rad(self): + vec = Vector3(1, 0, 0) + result = vec.rotate_z_rad(math.pi / 2) + self.assertEqual(result, (0, 1, 0)) + + def test_rotate_z_ip(self): + v = Vector3(1, 0, 0) + self.assertEqual(v.rotate_z_ip(90), None) + self.assertAlmostEqual(v.x, 0) + self.assertAlmostEqual(v.y, 1) + self.assertEqual(v.z, 0) + v = Vector3(-1, -1, 1) + v.rotate_z_ip(-90) + self.assertAlmostEqual(v.x, -1) + self.assertAlmostEqual(v.y, 1) + self.assertEqual(v.z, 1) + + def test_rotate_z_ip_rad(self): + vec = Vector3(1, 0, 0) + vec.rotate_z_ip_rad(math.pi / 2) + self.assertEqual(vec, (0, 1, 0)) + + def test_normalize(self): + v = self.v1.normalize() + # length is 1 + self.assertAlmostEqual(v.x * v.x + v.y * v.y + v.z * v.z, 1.0) + # v1 is unchanged + self.assertEqual(self.v1.x, self.l1[0]) + self.assertEqual(self.v1.y, self.l1[1]) + self.assertEqual(self.v1.z, self.l1[2]) + # v2 is parallel to v1 (tested via cross product) + cross = ( + (self.v1.y * v.z - self.v1.z * v.y) ** 2 + + (self.v1.z * v.x - self.v1.x * v.z) ** 2 + + (self.v1.x * v.y - self.v1.y * v.x) ** 2 + ) + self.assertAlmostEqual(cross, 0.0) + self.assertRaises(ValueError, lambda: self.zeroVec.normalize()) + + def test_normalize_ip(self): + v = +self.v1 + # v has length != 1 before normalizing + self.assertNotEqual(v.x * v.x + v.y * v.y + v.z * v.z, 1.0) + # inplace operations should return None + self.assertEqual(v.normalize_ip(), None) + # length is 1 + self.assertAlmostEqual(v.x * v.x + v.y * v.y + v.z * v.z, 1.0) + # v2 is parallel to v1 (tested via cross product) + cross = ( + (self.v1.y * v.z - self.v1.z * v.y) ** 2 + + (self.v1.z * v.x - self.v1.x * v.z) ** 2 + + (self.v1.x * v.y - self.v1.y * v.x) ** 2 + ) + self.assertAlmostEqual(cross, 0.0) + self.assertRaises(ValueError, lambda: self.zeroVec.normalize_ip()) + + def test_is_normalized(self): + self.assertEqual(self.v1.is_normalized(), False) + v = self.v1.normalize() + self.assertEqual(v.is_normalized(), True) + self.assertEqual(self.e2.is_normalized(), True) + self.assertEqual(self.zeroVec.is_normalized(), False) + + def test_cross(self): + def cross(a, b): + return Vector3( + a[1] * b[2] - a[2] * b[1], + a[2] * b[0] - a[0] * b[2], + a[0] * b[1] - a[1] * b[0], + ) + + self.assertEqual(self.v1.cross(self.v2), cross(self.v1, self.v2)) + self.assertEqual(self.v1.cross(self.l2), cross(self.v1, self.l2)) + self.assertEqual(self.v1.cross(self.t2), cross(self.v1, self.t2)) + self.assertEqual(self.v1.cross(self.v2), -self.v2.cross(self.v1)) + self.assertEqual(self.v1.cross(self.v1), self.zeroVec) + + def test_dot(self): + self.assertAlmostEqual( + self.v1.dot(self.v2), + self.v1.x * self.v2.x + self.v1.y * self.v2.y + self.v1.z * self.v2.z, + ) + self.assertAlmostEqual( + self.v1.dot(self.l2), + self.v1.x * self.l2[0] + self.v1.y * self.l2[1] + self.v1.z * self.l2[2], + ) + self.assertAlmostEqual( + self.v1.dot(self.t2), + self.v1.x * self.t2[0] + self.v1.y * self.t2[1] + self.v1.z * self.t2[2], + ) + self.assertAlmostEqual(self.v1.dot(self.v2), self.v2.dot(self.v1)) + self.assertAlmostEqual(self.v1.dot(self.v2), self.v1 * self.v2) + + def test_angle_to(self): + self.assertEqual(Vector3(1, 1, 0).angle_to((-1, 1, 0)), 90) + self.assertEqual(Vector3(1, 0, 0).angle_to((0, 0, -1)), 90) + self.assertEqual(Vector3(1, 0, 0).angle_to((-1, 0, 1)), 135) + self.assertEqual(abs(Vector3(1, 0, 1).angle_to((-1, 0, -1))), 180) + # if we rotate v1 by the angle_to v2 around their cross product + # we should look in the same direction + self.assertEqual( + self.v1.rotate( + self.v1.angle_to(self.v2), self.v1.cross(self.v2) + ).normalize(), + self.v2.normalize(), + ) + + def test_scale_to_length(self): + v = Vector3(1, 1, 1) + v.scale_to_length(2.5) + self.assertEqual(v, Vector3(2.5, 2.5, 2.5) / math.sqrt(3)) + self.assertRaises(ValueError, lambda: self.zeroVec.scale_to_length(1)) + self.assertEqual(v.scale_to_length(0), None) + self.assertEqual(v, self.zeroVec) + + def test_length(self): + self.assertEqual(Vector3(3, 4, 5).length(), math.sqrt(3 * 3 + 4 * 4 + 5 * 5)) + self.assertEqual(Vector3(-3, 4, 5).length(), math.sqrt(-3 * -3 + 4 * 4 + 5 * 5)) + self.assertEqual(self.zeroVec.length(), 0) + + def test_length_squared(self): + self.assertEqual(Vector3(3, 4, 5).length_squared(), 3 * 3 + 4 * 4 + 5 * 5) + self.assertEqual(Vector3(-3, 4, 5).length_squared(), -3 * -3 + 4 * 4 + 5 * 5) + self.assertEqual(self.zeroVec.length_squared(), 0) + + def test_reflect(self): + v = Vector3(1, -1, 1) + n = Vector3(0, 1, 0) + self.assertEqual(v.reflect(n), Vector3(1, 1, 1)) + self.assertEqual(v.reflect(3 * n), v.reflect(n)) + self.assertEqual(v.reflect(-v), -v) + self.assertRaises(ValueError, lambda: v.reflect(self.zeroVec)) + + def test_reflect_ip(self): + v1 = Vector3(1, -1, 1) + v2 = Vector3(v1) + n = Vector3(0, 1, 0) + self.assertEqual(v2.reflect_ip(n), None) + self.assertEqual(v2, Vector3(1, 1, 1)) + v2 = Vector3(v1) + v2.reflect_ip(3 * n) + self.assertEqual(v2, v1.reflect(n)) + v2 = Vector3(v1) + v2.reflect_ip(-v1) + self.assertEqual(v2, -v1) + self.assertRaises(ValueError, lambda: v2.reflect_ip(self.zeroVec)) + + def test_distance_to(self): + diff = self.v1 - self.v2 + self.assertEqual(self.e1.distance_to(self.e2), math.sqrt(2)) + self.assertEqual( + self.v1.distance_to(self.v2), + math.sqrt(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z), + ) + self.assertEqual(self.v1.distance_to(self.v1), 0) + self.assertEqual(self.v1.distance_to(self.v2), self.v2.distance_to(self.v1)) + + def test_distance_squared_to(self): + diff = self.v1 - self.v2 + self.assertEqual(self.e1.distance_squared_to(self.e2), 2) + self.assertAlmostEqual( + self.v1.distance_squared_to(self.v2), + diff.x * diff.x + diff.y * diff.y + diff.z * diff.z, + ) + self.assertEqual(self.v1.distance_squared_to(self.v1), 0) + self.assertEqual( + self.v1.distance_squared_to(self.v2), self.v2.distance_squared_to(self.v1) + ) + + def test_swizzle(self): + self.assertTrue(hasattr(pygame.math, "enable_swizzling")) + self.assertTrue(hasattr(pygame.math, "disable_swizzling")) + # swizzling enabled by default + pygame.math.disable_swizzling() + self.assertRaises(AttributeError, lambda: self.v1.yx) + pygame.math.enable_swizzling() + + self.assertEqual(self.v1.yxz, (self.v1.y, self.v1.x, self.v1.z)) + self.assertEqual( + self.v1.xxyyzzxyz, + ( + self.v1.x, + self.v1.x, + self.v1.y, + self.v1.y, + self.v1.z, + self.v1.z, + self.v1.x, + self.v1.y, + self.v1.z, + ), + ) + self.v1.xyz = self.t2 + self.assertEqual(self.v1, self.t2) + self.v1.zxy = self.t2 + self.assertEqual(self.v1, (self.t2[1], self.t2[2], self.t2[0])) + self.v1.yz = self.t2[:2] + self.assertEqual(self.v1, (self.t2[1], self.t2[0], self.t2[1])) + self.assertEqual(type(self.v1), Vector3) + + @unittest.skipIf(IS_PYPY, "known pypy failure") + def test_invalid_swizzle(self): + def invalidSwizzleX(): + Vector3().xx = (1, 2) + + def invalidSwizzleY(): + Vector3().yy = (1, 2) + + def invalidSwizzleZ(): + Vector3().zz = (1, 2) + + def invalidSwizzleW(): + Vector3().ww = (1, 2) + + self.assertRaises(AttributeError, invalidSwizzleX) + self.assertRaises(AttributeError, invalidSwizzleY) + self.assertRaises(AttributeError, invalidSwizzleZ) + self.assertRaises(AttributeError, invalidSwizzleW) + + def invalidAssignment(): + Vector3().xy = 3 + + self.assertRaises(TypeError, invalidAssignment) + + def test_swizzle_return_types(self): + self.assertEqual(type(self.v1.x), float) + self.assertEqual(type(self.v1.xy), Vector2) + self.assertEqual(type(self.v1.xyz), Vector3) + # but we don't have vector4 or above... so tuple. + self.assertEqual(type(self.v1.xyxy), tuple) + self.assertEqual(type(self.v1.xyxyx), tuple) + + def test_dir_works(self): + # not every single one of the attributes... + attributes = set( + ["lerp", "normalize", "normalize_ip", "reflect", "slerp", "x", "y"] + ) + # check if this selection of attributes are all there. + self.assertTrue(attributes.issubset(set(dir(self.v1)))) + + def test_elementwise(self): + # behaviour for "elementwise op scalar" + self.assertEqual( + self.v1.elementwise() + self.s1, + (self.v1.x + self.s1, self.v1.y + self.s1, self.v1.z + self.s1), + ) + self.assertEqual( + self.v1.elementwise() - self.s1, + (self.v1.x - self.s1, self.v1.y - self.s1, self.v1.z - self.s1), + ) + self.assertEqual( + self.v1.elementwise() * self.s2, + (self.v1.x * self.s2, self.v1.y * self.s2, self.v1.z * self.s2), + ) + self.assertEqual( + self.v1.elementwise() / self.s2, + (self.v1.x / self.s2, self.v1.y / self.s2, self.v1.z / self.s2), + ) + self.assertEqual( + self.v1.elementwise() // self.s1, + (self.v1.x // self.s1, self.v1.y // self.s1, self.v1.z // self.s1), + ) + self.assertEqual( + self.v1.elementwise() ** self.s1, + (self.v1.x ** self.s1, self.v1.y ** self.s1, self.v1.z ** self.s1), + ) + self.assertEqual( + self.v1.elementwise() % self.s1, + (self.v1.x % self.s1, self.v1.y % self.s1, self.v1.z % self.s1), + ) + self.assertEqual( + self.v1.elementwise() > self.s1, + self.v1.x > self.s1 and self.v1.y > self.s1 and self.v1.z > self.s1, + ) + self.assertEqual( + self.v1.elementwise() < self.s1, + self.v1.x < self.s1 and self.v1.y < self.s1 and self.v1.z < self.s1, + ) + self.assertEqual( + self.v1.elementwise() == self.s1, + self.v1.x == self.s1 and self.v1.y == self.s1 and self.v1.z == self.s1, + ) + self.assertEqual( + self.v1.elementwise() != self.s1, + self.v1.x != self.s1 and self.v1.y != self.s1 and self.v1.z != self.s1, + ) + self.assertEqual( + self.v1.elementwise() >= self.s1, + self.v1.x >= self.s1 and self.v1.y >= self.s1 and self.v1.z >= self.s1, + ) + self.assertEqual( + self.v1.elementwise() <= self.s1, + self.v1.x <= self.s1 and self.v1.y <= self.s1 and self.v1.z <= self.s1, + ) + # behaviour for "scalar op elementwise" + self.assertEqual(5 + self.v1.elementwise(), Vector3(5, 5, 5) + self.v1) + self.assertEqual(3.5 - self.v1.elementwise(), Vector3(3.5, 3.5, 3.5) - self.v1) + self.assertEqual(7.5 * self.v1.elementwise(), 7.5 * self.v1) + self.assertEqual( + -3.5 / self.v1.elementwise(), + (-3.5 / self.v1.x, -3.5 / self.v1.y, -3.5 / self.v1.z), + ) + self.assertEqual( + -3.5 // self.v1.elementwise(), + (-3.5 // self.v1.x, -3.5 // self.v1.y, -3.5 // self.v1.z), + ) + self.assertEqual( + -3.5 ** self.v1.elementwise(), + (-3.5 ** self.v1.x, -3.5 ** self.v1.y, -3.5 ** self.v1.z), + ) + self.assertEqual( + 3 % self.v1.elementwise(), (3 % self.v1.x, 3 % self.v1.y, 3 % self.v1.z) + ) + self.assertEqual( + 2 < self.v1.elementwise(), 2 < self.v1.x and 2 < self.v1.y and 2 < self.v1.z + ) + self.assertEqual( + 2 > self.v1.elementwise(), 2 > self.v1.x and 2 > self.v1.y and 2 > self.v1.z + ) + self.assertEqual( + 1 == self.v1.elementwise(), + 1 == self.v1.x and 1 == self.v1.y and 1 == self.v1.z, + ) + self.assertEqual( + 1 != self.v1.elementwise(), + 1 != self.v1.x and 1 != self.v1.y and 1 != self.v1.z, + ) + self.assertEqual( + 2 <= self.v1.elementwise(), + 2 <= self.v1.x and 2 <= self.v1.y and 2 <= self.v1.z, + ) + self.assertEqual( + -7 >= self.v1.elementwise(), + -7 >= self.v1.x and -7 >= self.v1.y and -7 >= self.v1.z, + ) + self.assertEqual( + -7 != self.v1.elementwise(), + -7 != self.v1.x and -7 != self.v1.y and -7 != self.v1.z, + ) + + # behaviour for "elementwise op vector" + self.assertEqual(type(self.v1.elementwise() * self.v2), type(self.v1)) + self.assertEqual(self.v1.elementwise() + self.v2, self.v1 + self.v2) + self.assertEqual(self.v1.elementwise() + self.v2, self.v1 + self.v2) + self.assertEqual(self.v1.elementwise() - self.v2, self.v1 - self.v2) + self.assertEqual( + self.v1.elementwise() * self.v2, + (self.v1.x * self.v2.x, self.v1.y * self.v2.y, self.v1.z * self.v2.z), + ) + self.assertEqual( + self.v1.elementwise() / self.v2, + (self.v1.x / self.v2.x, self.v1.y / self.v2.y, self.v1.z / self.v2.z), + ) + self.assertEqual( + self.v1.elementwise() // self.v2, + (self.v1.x // self.v2.x, self.v1.y // self.v2.y, self.v1.z // self.v2.z), + ) + self.assertEqual( + self.v1.elementwise() ** self.v2, + (self.v1.x ** self.v2.x, self.v1.y ** self.v2.y, self.v1.z ** self.v2.z), + ) + self.assertEqual( + self.v1.elementwise() % self.v2, + (self.v1.x % self.v2.x, self.v1.y % self.v2.y, self.v1.z % self.v2.z), + ) + self.assertEqual( + self.v1.elementwise() > self.v2, + self.v1.x > self.v2.x and self.v1.y > self.v2.y and self.v1.z > self.v2.z, + ) + self.assertEqual( + self.v1.elementwise() < self.v2, + self.v1.x < self.v2.x and self.v1.y < self.v2.y and self.v1.z < self.v2.z, + ) + self.assertEqual( + self.v1.elementwise() >= self.v2, + self.v1.x >= self.v2.x + and self.v1.y >= self.v2.y + and self.v1.z >= self.v2.z, + ) + self.assertEqual( + self.v1.elementwise() <= self.v2, + self.v1.x <= self.v2.x + and self.v1.y <= self.v2.y + and self.v1.z <= self.v2.z, + ) + self.assertEqual( + self.v1.elementwise() == self.v2, + self.v1.x == self.v2.x + and self.v1.y == self.v2.y + and self.v1.z == self.v2.z, + ) + self.assertEqual( + self.v1.elementwise() != self.v2, + self.v1.x != self.v2.x + and self.v1.y != self.v2.y + and self.v1.z != self.v2.z, + ) + # behaviour for "vector op elementwise" + self.assertEqual(self.v2 + self.v1.elementwise(), self.v2 + self.v1) + self.assertEqual(self.v2 - self.v1.elementwise(), self.v2 - self.v1) + self.assertEqual( + self.v2 * self.v1.elementwise(), + (self.v2.x * self.v1.x, self.v2.y * self.v1.y, self.v2.z * self.v1.z), + ) + self.assertEqual( + self.v2 / self.v1.elementwise(), + (self.v2.x / self.v1.x, self.v2.y / self.v1.y, self.v2.z / self.v1.z), + ) + self.assertEqual( + self.v2 // self.v1.elementwise(), + (self.v2.x // self.v1.x, self.v2.y // self.v1.y, self.v2.z // self.v1.z), + ) + self.assertEqual( + self.v2 ** self.v1.elementwise(), + (self.v2.x ** self.v1.x, self.v2.y ** self.v1.y, self.v2.z ** self.v1.z), + ) + self.assertEqual( + self.v2 % self.v1.elementwise(), + (self.v2.x % self.v1.x, self.v2.y % self.v1.y, self.v2.z % self.v1.z), + ) + self.assertEqual( + self.v2 < self.v1.elementwise(), + self.v2.x < self.v1.x and self.v2.y < self.v1.y and self.v2.z < self.v1.z, + ) + self.assertEqual( + self.v2 > self.v1.elementwise(), + self.v2.x > self.v1.x and self.v2.y > self.v1.y and self.v2.z > self.v1.z, + ) + self.assertEqual( + self.v2 <= self.v1.elementwise(), + self.v2.x <= self.v1.x + and self.v2.y <= self.v1.y + and self.v2.z <= self.v1.z, + ) + self.assertEqual( + self.v2 >= self.v1.elementwise(), + self.v2.x >= self.v1.x + and self.v2.y >= self.v1.y + and self.v2.z >= self.v1.z, + ) + self.assertEqual( + self.v2 == self.v1.elementwise(), + self.v2.x == self.v1.x + and self.v2.y == self.v1.y + and self.v2.z == self.v1.z, + ) + self.assertEqual( + self.v2 != self.v1.elementwise(), + self.v2.x != self.v1.x + and self.v2.y != self.v1.y + and self.v2.z != self.v1.z, + ) + + # behaviour for "elementwise op elementwise" + self.assertEqual( + self.v2.elementwise() + self.v1.elementwise(), self.v2 + self.v1 + ) + self.assertEqual( + self.v2.elementwise() - self.v1.elementwise(), self.v2 - self.v1 + ) + self.assertEqual( + self.v2.elementwise() * self.v1.elementwise(), + (self.v2.x * self.v1.x, self.v2.y * self.v1.y, self.v2.z * self.v1.z), + ) + self.assertEqual( + self.v2.elementwise() / self.v1.elementwise(), + (self.v2.x / self.v1.x, self.v2.y / self.v1.y, self.v2.z / self.v1.z), + ) + self.assertEqual( + self.v2.elementwise() // self.v1.elementwise(), + (self.v2.x // self.v1.x, self.v2.y // self.v1.y, self.v2.z // self.v1.z), + ) + self.assertEqual( + self.v2.elementwise() ** self.v1.elementwise(), + (self.v2.x ** self.v1.x, self.v2.y ** self.v1.y, self.v2.z ** self.v1.z), + ) + self.assertEqual( + self.v2.elementwise() % self.v1.elementwise(), + (self.v2.x % self.v1.x, self.v2.y % self.v1.y, self.v2.z % self.v1.z), + ) + self.assertEqual( + self.v2.elementwise() < self.v1.elementwise(), + self.v2.x < self.v1.x and self.v2.y < self.v1.y and self.v2.z < self.v1.z, + ) + self.assertEqual( + self.v2.elementwise() > self.v1.elementwise(), + self.v2.x > self.v1.x and self.v2.y > self.v1.y and self.v2.z > self.v1.z, + ) + self.assertEqual( + self.v2.elementwise() <= self.v1.elementwise(), + self.v2.x <= self.v1.x + and self.v2.y <= self.v1.y + and self.v2.z <= self.v1.z, + ) + self.assertEqual( + self.v2.elementwise() >= self.v1.elementwise(), + self.v2.x >= self.v1.x + and self.v2.y >= self.v1.y + and self.v2.z >= self.v1.z, + ) + self.assertEqual( + self.v2.elementwise() == self.v1.elementwise(), + self.v2.x == self.v1.x + and self.v2.y == self.v1.y + and self.v2.z == self.v1.z, + ) + self.assertEqual( + self.v2.elementwise() != self.v1.elementwise(), + self.v2.x != self.v1.x + and self.v2.y != self.v1.y + and self.v2.z != self.v1.z, + ) + + # other behaviour + self.assertEqual( + abs(self.v1.elementwise()), (abs(self.v1.x), abs(self.v1.y), abs(self.v1.z)) + ) + self.assertEqual(-self.v1.elementwise(), -self.v1) + self.assertEqual(+self.v1.elementwise(), +self.v1) + self.assertEqual(bool(self.v1.elementwise()), bool(self.v1)) + self.assertEqual(bool(Vector3().elementwise()), bool(Vector3())) + self.assertEqual(self.zeroVec.elementwise() ** 0, (1, 1, 1)) + self.assertRaises(ValueError, lambda: pow(Vector3(-1, 0, 0).elementwise(), 1.2)) + self.assertRaises(ZeroDivisionError, lambda: self.zeroVec.elementwise() ** -1) + self.assertRaises(ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() / 0) + self.assertRaises( + ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() // 0 + ) + self.assertRaises(ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() % 0) + self.assertRaises( + ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() / self.zeroVec + ) + self.assertRaises( + ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() // self.zeroVec + ) + self.assertRaises( + ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() % self.zeroVec + ) + self.assertRaises(ZeroDivisionError, lambda: 2 / self.zeroVec.elementwise()) + self.assertRaises(ZeroDivisionError, lambda: 2 // self.zeroVec.elementwise()) + self.assertRaises(ZeroDivisionError, lambda: 2 % self.zeroVec.elementwise()) + + def test_slerp(self): + self.assertRaises(ValueError, lambda: self.zeroVec.slerp(self.v1, 0.5)) + self.assertRaises(ValueError, lambda: self.v1.slerp(self.zeroVec, 0.5)) + self.assertRaises(ValueError, lambda: self.zeroVec.slerp(self.zeroVec, 0.5)) + steps = 10 + angle_step = self.e1.angle_to(self.e2) / steps + for i, u in ( + (i, self.e1.slerp(self.e2, i / float(steps))) for i in range(steps + 1) + ): + self.assertAlmostEqual(u.length(), 1) + self.assertAlmostEqual(self.e1.angle_to(u), i * angle_step) + self.assertEqual(u, self.e2) + + v1 = Vector3(100, 0, 0) + v2 = Vector3(0, 10, 7) + radial_factor = v2.length() / v1.length() + for i, u in ((i, v1.slerp(v2, -i / float(steps))) for i in range(steps + 1)): + self.assertAlmostEqual( + u.length(), + (v2.length() - v1.length()) * (float(i) / steps) + v1.length(), + ) + self.assertEqual(u, v2) + self.assertEqual(v1.slerp(v1, 0.5), v1) + self.assertEqual(v2.slerp(v2, 0.5), v2) + self.assertRaises(ValueError, lambda: v1.slerp(-v1, 0.5)) + + def test_lerp(self): + v1 = Vector3(0, 0, 0) + v2 = Vector3(10, 10, 10) + self.assertEqual(v1.lerp(v2, 0.5), (5, 5, 5)) + self.assertRaises(ValueError, lambda: v1.lerp(v2, 2.5)) + + v1 = Vector3(-10, -5, -20) + v2 = Vector3(10, 10, -20) + self.assertEqual(v1.lerp(v2, 0.5), (0, 2.5, -20)) + + def test_spherical(self): + v = Vector3() + v.from_spherical(self.v1.as_spherical()) + self.assertEqual(self.v1, v) + self.assertEqual(self.e1.as_spherical(), (1, 90, 0)) + self.assertEqual(self.e2.as_spherical(), (1, 90, 90)) + self.assertEqual(self.e3.as_spherical(), (1, 0, 0)) + self.assertEqual((2 * self.e2).as_spherical(), (2, 90, 90)) + self.assertRaises(TypeError, lambda: v.from_spherical((None, None, None))) + self.assertRaises(TypeError, lambda: v.from_spherical("abc")) + self.assertRaises(TypeError, lambda: v.from_spherical((None, 1, 2))) + self.assertRaises(TypeError, lambda: v.from_spherical((1, 2, 3, 4))) + self.assertRaises(TypeError, lambda: v.from_spherical((1, 2))) + self.assertRaises(TypeError, lambda: v.from_spherical(1, 2, 3)) + v.from_spherical((0.5, 90, 90)) + self.assertEqual(v, 0.5 * self.e2) + + def test_inplace_operators(self): + + v = Vector3(1, 1, 1) + v *= 2 + self.assertEqual(v, (2.0, 2.0, 2.0)) + + v = Vector3(4, 4, 4) + v /= 2 + self.assertEqual(v, (2.0, 2.0, 2.0)) + + v = Vector3(3.0, 3.0, 3.0) + v -= (1, 1, 1) + self.assertEqual(v, (2.0, 2.0, 2.0)) + + v = Vector3(3.0, 3.0, 3.0) + v += (1, 1, 1) + self.assertEqual(v, (4.0, 4.0, 4.0)) + + def test_pickle(self): + import pickle + + v2 = Vector2(1, 2) + v3 = Vector3(1, 2, 3) + self.assertEqual(pickle.loads(pickle.dumps(v2)), v2) + self.assertEqual(pickle.loads(pickle.dumps(v3)), v3) + + def test_subclass_operation(self): + class Vector(pygame.math.Vector3): + pass + + v = Vector(2.0, 2.0, 2.0) + v *= 2 + self.assertEqual(v, (4.0, 4.0, 4.0)) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/midi_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/midi_test.py new file mode 100644 index 0000000..5b55a68 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/midi_test.py @@ -0,0 +1,469 @@ +import unittest +import os +import sys +import time + +import pygame +import pygame.midi +import pygame.compat +from pygame.locals import * + + +class MidiInputTest(unittest.TestCase): + __tags__ = ["interactive"] + + def setUp(self): + pygame.midi.init() + in_id = pygame.midi.get_default_input_id() + if in_id != -1: + self.midi_input = pygame.midi.Input(in_id) + else: + self.midi_input = None + + def tearDown(self): + if self.midi_input: + self.midi_input.close() + pygame.midi.quit() + + def test_Input(self): + i = pygame.midi.get_default_input_id() + if self.midi_input: + self.assertEqual(self.midi_input.device_id, i) + + # try feeding it an input id. + i = pygame.midi.get_default_output_id() + + # can handle some invalid input too. + self.assertRaises(pygame.midi.MidiException, pygame.midi.Input, i) + self.assertRaises(pygame.midi.MidiException, pygame.midi.Input, 9009) + self.assertRaises(pygame.midi.MidiException, pygame.midi.Input, -1) + self.assertRaises(TypeError, pygame.midi.Input, "1234") + self.assertRaises(OverflowError, pygame.midi.Input, pow(2, 99)) + + def test_poll(self): + + if not self.midi_input: + self.skipTest("No midi Input device") + + self.assertFalse(self.midi_input.poll()) + # TODO fake some incoming data + + pygame.midi.quit() + self.assertRaises(RuntimeError, self.midi_input.poll) + # set midi_input to None to avoid error in tearDown + self.midi_input = None + + def test_read(self): + + if not self.midi_input: + self.skipTest("No midi Input device") + + read = self.midi_input.read(5) + self.assertEqual(read, []) + # TODO fake some incoming data + + pygame.midi.quit() + self.assertRaises(RuntimeError, self.midi_input.read, 52) + # set midi_input to None to avoid error in tearDown + self.midi_input = None + + def test_close(self): + if not self.midi_input: + self.skipTest("No midi Input device") + + self.assertIsNotNone(self.midi_input._input) + self.midi_input.close() + self.assertIsNone(self.midi_input._input) + + +class MidiOutputTest(unittest.TestCase): + __tags__ = ["interactive"] + + def setUp(self): + pygame.midi.init() + m_out_id = pygame.midi.get_default_output_id() + if m_out_id != -1: + self.midi_output = pygame.midi.Output(m_out_id) + else: + self.midi_output = None + + def tearDown(self): + if self.midi_output: + self.midi_output.close() + pygame.midi.quit() + + def test_Output(self): + i = pygame.midi.get_default_output_id() + if self.midi_output: + self.assertEqual(self.midi_output.device_id, i) + + # try feeding it an input id. + i = pygame.midi.get_default_input_id() + + # can handle some invalid input too. + self.assertRaises(pygame.midi.MidiException, pygame.midi.Output, i) + self.assertRaises(pygame.midi.MidiException, pygame.midi.Output, 9009) + self.assertRaises(pygame.midi.MidiException, pygame.midi.Output, -1) + self.assertRaises(TypeError, pygame.midi.Output, "1234") + self.assertRaises(OverflowError, pygame.midi.Output, pow(2, 99)) + + def test_note_off(self): + if self.midi_output: + out = self.midi_output + out.note_on(5, 30, 0) + out.note_off(5, 30, 0) + with self.assertRaises(ValueError) as cm: + out.note_off(5, 30, 25) + self.assertEqual(str(cm.exception), "Channel not between 0 and 15.") + with self.assertRaises(ValueError) as cm: + out.note_off(5, 30, -1) + self.assertEqual(str(cm.exception), "Channel not between 0 and 15.") + + def test_note_on(self): + if self.midi_output: + out = self.midi_output + out.note_on(5, 30, 0) + out.note_on(5, 42, 10) + with self.assertRaises(ValueError) as cm: + out.note_on(5, 30, 25) + self.assertEqual(str(cm.exception), "Channel not between 0 and 15.") + with self.assertRaises(ValueError) as cm: + out.note_on(5, 30, -1) + self.assertEqual(str(cm.exception), "Channel not between 0 and 15.") + + def test_set_instrument(self): + + if not self.midi_output: + self.skipTest("No midi device") + out = self.midi_output + out.set_instrument(5) + out.set_instrument(42, channel=2) + with self.assertRaises(ValueError) as cm: + out.set_instrument(-6) + self.assertEqual(str(cm.exception), "Undefined instrument id: -6") + with self.assertRaises(ValueError) as cm: + out.set_instrument(156) + self.assertEqual(str(cm.exception), "Undefined instrument id: 156") + with self.assertRaises(ValueError) as cm: + out.set_instrument(5, -1) + self.assertEqual(str(cm.exception), "Channel not between 0 and 15.") + with self.assertRaises(ValueError) as cm: + out.set_instrument(5, 16) + self.assertEqual(str(cm.exception), "Channel not between 0 and 15.") + + def test_write(self): + if not self.midi_output: + self.skipTest("No midi device") + + out = self.midi_output + out.write([[[0xC0, 0, 0], 20000]]) + # is equivalent to + out.write([[[0xC0], 20000]]) + # example from the docstring : + # 1. choose program change 1 at time 20000 and + # 2. send note 65 with velocity 100 500 ms later + out.write([[[0xC0, 0, 0], 20000], [[0x90, 60, 100], 20500]]) + + out.write([]) + verrry_long = [[[0x90, 60, i % 100], 20000 + 100 * i] for i in range(1024)] + out.write(verrry_long) + + too_long = [[[0x90, 60, i % 100], 20000 + 100 * i] for i in range(1025)] + self.assertRaises(IndexError, out.write, too_long) + # test wrong data + with self.assertRaises(TypeError) as cm: + out.write("Non sens ?") + error_msg = "unsupported operand type(s) for &: 'str' and 'int'" + self.assertEqual(str(cm.exception), error_msg) + + with self.assertRaises(TypeError) as cm: + out.write(["Hey what's that?"]) + self.assertEqual(str(cm.exception), error_msg) + + def test_write_short(self): + if not self.midi_output: + self.skipTest("No midi device") + + out = self.midi_output + # program change + out.write_short(0xC0) + # put a note on, then off. + out.write_short(0x90, 65, 100) + out.write_short(0x80, 65, 100) + out.write_short(0x90) + + def test_write_sys_ex(self): + if not self.midi_output: + self.skipTest("No midi device") + + out = self.midi_output + out.write_sys_ex(pygame.midi.time(), [0xF0, 0x7D, 0x10, 0x11, 0x12, 0x13, 0xF7]) + + def test_pitch_bend(self): + # FIXME : pitch_bend in the code, but not in documentation + if not self.midi_output: + self.skipTest("No midi device") + + out = self.midi_output + with self.assertRaises(ValueError) as cm: + out.pitch_bend(5, channel=-1) + self.assertEqual(str(cm.exception), "Channel not between 0 and 15.") + with self.assertRaises(ValueError) as cm: + out.pitch_bend(5, channel=16) + with self.assertRaises(ValueError) as cm: + out.pitch_bend(-10001, 1) + self.assertEqual( + str(cm.exception), + "Pitch bend value must be between " "-8192 and +8191, not -10001.", + ) + with self.assertRaises(ValueError) as cm: + out.pitch_bend(10665, 2) + + def test_close(self): + if not self.midi_output: + self.skipTest("No midi device") + self.assertIsNotNone(self.midi_output._output) + self.midi_output.close() + self.assertIsNone(self.midi_output._output) + + def test_abort(self): + if not self.midi_output: + self.skipTest("No midi device") + self.assertEqual(self.midi_output._aborted, 0) + self.midi_output.abort() + self.assertEqual(self.midi_output._aborted, 1) + + +class MidiModuleTest(unittest.TestCase): + """Midi module tests that require midi hardware or midi.init(). + + See MidiModuleNonInteractiveTest for non-interactive module tests. + """ + + __tags__ = ["interactive"] + + def setUp(self): + pygame.midi.init() + + def tearDown(self): + pygame.midi.quit() + + def test_get_count(self): + c = pygame.midi.get_count() + self.assertIsInstance(c, int) + self.assertTrue(c >= 0) + + def test_get_default_input_id(self): + + midin_id = pygame.midi.get_default_input_id() + # if there is a not None return make sure it is an int. + self.assertIsInstance(midin_id, int) + self.assertTrue(midin_id >= -1) + pygame.midi.quit() + self.assertRaises(RuntimeError, pygame.midi.get_default_output_id) + + def test_get_default_output_id(self): + + c = pygame.midi.get_default_output_id() + self.assertIsInstance(c, int) + self.assertTrue(c >= -1) + pygame.midi.quit() + self.assertRaises(RuntimeError, pygame.midi.get_default_output_id) + + def test_get_device_info(self): + + an_id = pygame.midi.get_default_output_id() + if an_id != -1: + interf, name, input, output, opened = pygame.midi.get_device_info(an_id) + self.assertEqual(output, 1) + self.assertEqual(input, 0) + self.assertEqual(opened, 0) + + an_in_id = pygame.midi.get_default_input_id() + if an_in_id != -1: + r = pygame.midi.get_device_info(an_in_id) + # if r is None, it means that the id is out of range. + interf, name, input, output, opened = r + + self.assertEqual(output, 0) + self.assertEqual(input, 1) + self.assertEqual(opened, 0) + out_of_range = pygame.midi.get_count() + for num in range(out_of_range): + self.assertIsNotNone(pygame.midi.get_device_info(num)) + info = pygame.midi.get_device_info(out_of_range) + self.assertIsNone(info) + + def test_init(self): + + pygame.midi.quit() + self.assertRaises(RuntimeError, pygame.midi.get_count) + # initialising many times should be fine. + pygame.midi.init() + pygame.midi.init() + pygame.midi.init() + pygame.midi.init() + + self.assertTrue(pygame.midi.get_init()) + + def test_quit(self): + + # It is safe to call this more than once. + pygame.midi.quit() + pygame.midi.init() + pygame.midi.quit() + pygame.midi.quit() + pygame.midi.init() + pygame.midi.init() + pygame.midi.quit() + + self.assertFalse(pygame.midi.get_init()) + + def test_get_init(self): + # Already initialized as pygame.midi.init() was called in setUp(). + self.assertTrue(pygame.midi.get_init()) + + def test_time(self): + + mtime = pygame.midi.time() + self.assertIsInstance(mtime, int) + # should be close to 2-3... since the timer is just init'd. + self.assertTrue(0 <= mtime < 100) + + +class MidiModuleNonInteractiveTest(unittest.TestCase): + """Midi module tests that do not require midi hardware or midi.init(). + + See MidiModuleTest for interactive module tests. + """ + + def test_midiin(self): + """Ensures the MIDIIN event id exists in the midi module. + + The MIDIIN event id can be accessed via the midi module for backward + compatibility. + """ + self.assertEqual(pygame.midi.MIDIIN, pygame.MIDIIN) + self.assertEqual(pygame.midi.MIDIIN, pygame.locals.MIDIIN) + + self.assertNotEqual(pygame.midi.MIDIIN, pygame.MIDIOUT) + self.assertNotEqual(pygame.midi.MIDIIN, pygame.locals.MIDIOUT) + + def test_midiout(self): + """Ensures the MIDIOUT event id exists in the midi module. + + The MIDIOUT event id can be accessed via the midi module for backward + compatibility. + """ + self.assertEqual(pygame.midi.MIDIOUT, pygame.MIDIOUT) + self.assertEqual(pygame.midi.MIDIOUT, pygame.locals.MIDIOUT) + + self.assertNotEqual(pygame.midi.MIDIOUT, pygame.MIDIIN) + self.assertNotEqual(pygame.midi.MIDIOUT, pygame.locals.MIDIIN) + + def test_MidiException(self): + """Ensures the MidiException is raised as expected.""" + + def raiseit(): + raise pygame.midi.MidiException("Hello Midi param") + + with self.assertRaises(pygame.midi.MidiException) as cm: + raiseit() + + self.assertEqual(cm.exception.parameter, "Hello Midi param") + + def test_midis2events(self): + """Ensures midi events are properly converted to pygame events.""" + # List/tuple indexes. + MIDI_DATA = 0 + MD_STATUS = 0 + MD_DATA1 = 1 + MD_DATA2 = 2 + MD_DATA3 = 3 + + TIMESTAMP = 1 + + # Midi events take the form of: + # ((status, data1, data2, data3), timestamp) + midi_events = ( + ((0xC0, 0, 1, 2), 20000), + ((0x90, 60, 1000, "string_data"), 20001), + (("0", "1", "2", "3"), "4"), + ) + expected_num_events = len(midi_events) + + # Test different device ids. + for device_id in range(3): + pg_events = pygame.midi.midis2events(midi_events, device_id) + + self.assertEqual(len(pg_events), expected_num_events) + + for i, pg_event in enumerate(pg_events): + # Get the original midi data for comparison. + midi_event = midi_events[i] + midi_event_data = midi_event[MIDI_DATA] + + # Can't directly check event instance as pygame.event.Event is + # a function. + # self.assertIsInstance(pg_event, pygame.event.Event) + self.assertEqual(pg_event.__class__.__name__, "Event") + self.assertEqual(pg_event.type, pygame.MIDIIN) + self.assertEqual(pg_event.status, midi_event_data[MD_STATUS]) + self.assertEqual(pg_event.data1, midi_event_data[MD_DATA1]) + self.assertEqual(pg_event.data2, midi_event_data[MD_DATA2]) + self.assertEqual(pg_event.data3, midi_event_data[MD_DATA3]) + self.assertEqual(pg_event.timestamp, midi_event[TIMESTAMP]) + self.assertEqual(pg_event.vice_id, device_id) + + def test_midis2events__missing_event_data(self): + """Ensures midi events with missing values are handled properly.""" + midi_event_missing_data = ((0xC0, 0, 1), 20000) + midi_event_missing_timestamp = ((0xC0, 0, 1, 2),) + + for midi_event in (midi_event_missing_data, midi_event_missing_timestamp): + with self.assertRaises(ValueError): + events = pygame.midi.midis2events([midi_event], 0) + + def test_midis2events__extra_event_data(self): + """Ensures midi events with extra values are handled properly.""" + midi_event_extra_data = ((0xC0, 0, 1, 2, "extra"), 20000) + midi_event_extra_timestamp = ((0xC0, 0, 1, 2), 20000, "extra") + + for midi_event in (midi_event_extra_data, midi_event_extra_timestamp): + with self.assertRaises(ValueError): + events = pygame.midi.midis2events([midi_event], 0) + + def test_midis2events__extra_event_data_missing_timestamp(self): + """Ensures midi events with extra data and no timestamps are handled + properly. + """ + midi_event_extra_data_no_timestamp = ((0xC0, 0, 1, 2, "extra"),) + + with self.assertRaises(ValueError): + events = pygame.midi.midis2events([midi_event_extra_data_no_timestamp], 0) + + def test_conversions(self): + """ of frequencies to midi note numbers and ansi note names. + """ + from pygame.midi import frequency_to_midi, midi_to_frequency, midi_to_ansi_note + + self.assertEqual(frequency_to_midi(27.5), 21) + self.assertEqual(frequency_to_midi(36.7), 26) + self.assertEqual(frequency_to_midi(4186.0), 108) + self.assertEqual(midi_to_frequency(21), 27.5) + self.assertEqual(midi_to_frequency(26), 36.7) + self.assertEqual(midi_to_frequency(108), 4186.0) + self.assertEqual(midi_to_ansi_note(21), "A0") + self.assertEqual(midi_to_ansi_note(71), "B4") + self.assertEqual(midi_to_ansi_note(82), "A#5") + self.assertEqual(midi_to_ansi_note(83), "B5") + self.assertEqual(midi_to_ansi_note(93), "A6") + self.assertEqual(midi_to_ansi_note(94), "A#6") + self.assertEqual(midi_to_ansi_note(95), "B6") + self.assertEqual(midi_to_ansi_note(96), "C7") + self.assertEqual(midi_to_ansi_note(102), "F#7") + self.assertEqual(midi_to_ansi_note(108), "C8") + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/mixer_music_tags.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/mixer_music_tags.py new file mode 100644 index 0000000..30f6893 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/mixer_music_tags.py @@ -0,0 +1,7 @@ +__tags__ = [] + +import pygame +import sys + +if "pygame.mixer_music" not in sys.modules: + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/mixer_music_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/mixer_music_test.py new file mode 100644 index 0000000..688281b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/mixer_music_test.py @@ -0,0 +1,336 @@ +# -*- coding: utf-8 -*- + +import os +import sys +import platform +import unittest + +from pygame.tests.test_utils import example_path +import pygame +from pygame.compat import as_unicode, unicode_, filesystem_encode + + +class MixerMusicModuleTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + # Initializing the mixer is slow, so minimize the times it is called. + pygame.mixer.init() + + @classmethod + def tearDownClass(cls): + pygame.mixer.quit() + + def setUp(cls): + # This makes sure the mixer is always initialized before each test (in + # case a test calls pygame.mixer.quit()). + if pygame.mixer.get_init() is None: + pygame.mixer.init() + + @unittest.skipIf( + "Darwin" in platform.system(), "SDL2_mixer not loading mp3 on travisci" + ) + def test_load_mp3(self): + "|tags:music|" + self.music_load("mp3") + + def test_load_ogg(self): + "|tags:music|" + self.music_load("ogg") + + def test_load_wav(self): + "|tags:music|" + self.music_load("wav") + + def music_load(self, format): + data_fname = example_path("data") + + path = os.path.join(data_fname, "house_lo.%s" % format) + if os.sep == "\\": + path = path.replace("\\", "\\\\") + umusfn = as_unicode(path) + bmusfn = filesystem_encode(umusfn) + + pygame.mixer.music.load(umusfn) + pygame.mixer.music.load(bmusfn) + + def test_load_object(self): + """test loading music from file-like objects.""" + formats = ["ogg", "wav"] + data_fname = example_path("data") + for f in formats: + path = os.path.join(data_fname, "house_lo.%s" % f) + if os.sep == "\\": + path = path.replace("\\", "\\\\") + bmusfn = filesystem_encode(path) + + with open(bmusfn, "rb") as musf: + pygame.mixer.music.load(musf) + + def test_load_unicode(self): + """test non-ASCII unicode path""" + import shutil + + ep = unicode_(example_path("data")) + temp_file = os.path.join(ep, u"你好.wav") + org_file = os.path.join(ep, u"house_lo.wav") + try: + with open(temp_file, "w") as f: + pass + os.remove(temp_file) + except IOError: + raise unittest.SkipTest("the path cannot be opened") + shutil.copy(org_file, temp_file) + try: + pygame.mixer.music.load(temp_file) + pygame.mixer.music.load(org_file) # unload + finally: + os.remove(temp_file) + + def test_unload(self): + import shutil + import tempfile + + ep = unicode_(example_path("data")) + org_file = os.path.join(ep, u"house_lo.wav") + tmpfd, tmppath = tempfile.mkstemp(".wav") + os.close(tmpfd) + shutil.copy(org_file, tmppath) + try: + pygame.mixer.music.load(tmppath) + pygame.mixer.music.unload() + finally: + os.remove(tmppath) + + @unittest.skipIf( + "Darwin" in platform.system(), "SDL2_mixer issue with mp3 files on Travis CI" + ) + def test_queue_mp3(self): + """Ensures queue() accepts mp3 files. + + |tags:music| + """ + filename = example_path(os.path.join("data", "house_lo.mp3")) + pygame.mixer.music.queue(filename) + + def test_queue_ogg(self): + """Ensures queue() accepts ogg files. + + |tags:music| + """ + filename = example_path(os.path.join("data", "house_lo.ogg")) + pygame.mixer.music.queue(filename) + + def test_queue_wav(self): + """Ensures queue() accepts wav files. + + |tags:music| + """ + filename = example_path(os.path.join("data", "house_lo.wav")) + pygame.mixer.music.queue(filename) + + def test_queue__multiple_calls(self): + """Ensures queue() can be called multiple times.""" + ogg_file = example_path(os.path.join("data", "house_lo.ogg")) + wav_file = example_path(os.path.join("data", "house_lo.wav")) + + pygame.mixer.music.queue(ogg_file) + pygame.mixer.music.queue(wav_file) + + def test_queue__no_file(self): + """Ensures queue() correctly handles missing the file argument.""" + with self.assertRaises(TypeError): + pygame.mixer.music.queue() + + def test_queue__invalid_sound_type(self): + """Ensures queue() correctly handles invalid file types.""" + not_a_sound_file = example_path(os.path.join("data", "city.png")) + + with self.assertRaises(pygame.error): + pygame.mixer.music.queue(not_a_sound_file) + + def test_queue__invalid_filename(self): + """Ensures queue() correctly handles invalid filenames.""" + with self.assertRaises(pygame.error): + pygame.mixer.music.queue("") + + def todo_test_stop(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer_music.stop: + + # Stops the music playback if it is currently playing. + + self.fail() + + def todo_test_rewind(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer_music.rewind: + + # Resets playback of the current music to the beginning. + + self.fail() + + def todo_test_get_pos(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer_music.get_pos: + + # This gets the number of milliseconds that the music has been playing + # for. The returned time only represents how long the music has been + # playing; it does not take into account any starting position + # offsets. + # + + self.fail() + + def todo_test_fadeout(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer_music.fadeout: + + # This will stop the music playback after it has been faded out over + # the specified time (measured in milliseconds). + # + # Note, that this function blocks until the music has faded out. + + self.fail() + + def todo_test_play(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer_music.play: + + # This will play the loaded music stream. If the music is already + # playing it will be restarted. + # + # The loops argument controls the number of repeats a music will play. + # play(5) will cause the music to played once, then repeated five + # times, for a total of six. If the loops is -1 then the music will + # repeat indefinitely. + # + # The starting position argument controls where in the music the song + # starts playing. The starting position is dependent on the format of + # music playing. MP3 and OGG use the position as time (in seconds). + # MOD music it is the pattern order number. Passing a startpos will + # raise a NotImplementedError if it cannot set the start position + # + + self.fail() + + def todo_test_load(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer_music.load: + + # This will load a music file and prepare it for playback. If a music + # stream is already playing it will be stopped. This does not start + # the music playing. + # + # Music can only be loaded from filenames, not python file objects + # like the other pygame loading functions. + # + + self.fail() + + def todo_test_get_volume(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer_music.get_volume: + + # Returns the current volume for the mixer. The value will be between + # 0.0 and 1.0. + # + + self.fail() + + def todo_test_set_endevent(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer_music.set_endevent: + + # This causes Pygame to signal (by means of the event queue) when the + # music is done playing. The argument determines the type of event + # that will be queued. + # + # The event will be queued every time the music finishes, not just the + # first time. To stop the event from being queued, call this method + # with no argument. + # + + self.fail() + + def todo_test_pause(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer_music.pause: + + # Temporarily stop playback of the music stream. It can be resumed + # with the pygame.mixer.music.unpause() function. + # + + self.fail() + + def todo_test_get_busy(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer_music.get_busy: + + # Returns True when the music stream is actively playing. When the + # music is idle this returns False. + # + + self.fail() + + def todo_test_get_endevent(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer_music.get_endevent: + + # Returns the event type to be sent every time the music finishes + # playback. If there is no endevent the function returns + # pygame.NOEVENT. + # + + self.fail() + + def todo_test_unpause(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer_music.unpause: + + # This will resume the playback of a music stream after it has been paused. + + self.fail() + + def todo_test_set_volume(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer_music.set_volume: + + # Set the volume of the music playback. The value argument is between + # 0.0 and 1.0. When new music is loaded the volume is reset. + # + + self.fail() + + def todo_test_set_pos(self): + + # __doc__ (as of 2010-24-05) for pygame.mixer_music.set_pos: + + # This sets the position in the music file where playback will start. The + # meaning of "pos", a float (or a number that can be converted to a float), + # depends on the music format. Newer versions of SDL_mixer have better + # positioning support than earlier. An SDLError is raised if a particular + # format does not support positioning. + # + + self.fail() + + def test_init(self): + """issue #955. unload music whenever mixer.quit() is called""" + import tempfile + import shutil + + testfile = example_path(os.path.join("data", "house_lo.wav")) + tempcopy = os.path.join(tempfile.gettempdir(), "tempfile.wav") + + for i in range(10): + pygame.mixer.init() + try: + shutil.copy2(testfile, tempcopy) + pygame.mixer.music.load(tempcopy) + pygame.mixer.quit() + finally: + os.remove(tempcopy) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/mixer_tags.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/mixer_tags.py new file mode 100644 index 0000000..06a9de2 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/mixer_tags.py @@ -0,0 +1,7 @@ +__tags__ = [] + +import pygame +import sys + +if "pygame.mixer" not in sys.modules: + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/mixer_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/mixer_test.py new file mode 100644 index 0000000..df31923 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/mixer_test.py @@ -0,0 +1,1124 @@ +# -*- coding: utf8 -*- + +import sys +import os +import unittest +import platform + +from pygame.tests.test_utils import example_path, AssertRaisesRegexMixin + +import pygame +from pygame import mixer +from pygame.compat import unicode_, as_bytes, bytes_ + + +IS_PYPY = "PyPy" == platform.python_implementation() + +################################### CONSTANTS ################################## + +FREQUENCIES = [11025, 22050, 44100, 48000] +SIZES = [-16, -8, 8, 16] +if pygame.get_sdl_version()[0] >= 2: + SIZES.append(32) + +CHANNELS = [1, 2] +BUFFERS = [3024] + +CONFIGS = [ + {"frequency": f, "size": s, "channels": c} + for f in FREQUENCIES + for s in SIZES + for c in CHANNELS +] +# Using all CONFIGS fails on a Mac; probably older SDL_mixer; we could do: +# if platform.system() == 'Darwin': +# But using all CONFIGS is very slow (> 10 sec for example) +# And probably, we don't need to be so exhaustive, hence: + +CONFIG = {"frequency": 22050, "size": -16, "channels": 2} # base config +if pygame.get_sdl_version()[0] >= 2: + CONFIG = {"frequency": 44100, "size": 32, "channels": 2} # base config + + +class InvalidBool(object): + """To help test invalid bool values.""" + + __nonzero__ = None + __bool__ = None + + +############################## MODULE LEVEL TESTS ############################## + + +class MixerModuleTest(unittest.TestCase): + def tearDown(self): + mixer.quit() + mixer.pre_init(0, 0, 0, 0) + + def test_init__keyword_args(self): + # note: this test used to loop over all CONFIGS, but it's very slow.. + mixer.init(**CONFIG) + mixer_conf = mixer.get_init() + + self.assertEqual(mixer_conf[0], CONFIG["frequency"]) + # Not all "sizes" are supported on all systems, hence "abs". + self.assertEqual(abs(mixer_conf[1]), abs(CONFIG["size"])) + self.assertEqual(mixer_conf[2], CONFIG["channels"]) + + def test_pre_init__keyword_args(self): + # note: this test used to loop over all CONFIGS, but it's very slow.. + mixer.pre_init(**CONFIG) + mixer.init() + + mixer_conf = mixer.get_init() + + self.assertEqual(mixer_conf[0], CONFIG["frequency"]) + # Not all "sizes" are supported on all systems, hence "abs". + self.assertEqual(abs(mixer_conf[1]), abs(CONFIG["size"])) + self.assertEqual(mixer_conf[2], CONFIG["channels"]) + + def test_pre_init__zero_values(self): + # Ensure that argument values of 0 are replaced with + # default values. No way to check buffer size though. + mixer.pre_init(22050, -8, 1) # Non default values + mixer.pre_init(0, 0, 0) # Should reset to default values + mixer.init() + self.assertEqual(mixer.get_init(), (44100, -16, 2)) + + def test_init__zero_values(self): + # Ensure that argument values of 0 are replaced with + # preset values. No way to check buffer size though. + mixer.pre_init(44100, 8, 1, allowedchanges=0) # None default values + mixer.init(0, 0, 0) + self.assertEqual(mixer.get_init(), (44100, 8, 1)) + + @unittest.skip("SDL_mixer bug") + def test_get_init__returns_exact_values_used_for_init(self): + # fix in 1.9 - I think it's a SDL_mixer bug. + + # TODO: When this bug is fixed, testing through every combination + # will be too slow so adjust as necessary, at the moment it + # breaks the loop after first failure + + for init_conf in CONFIGS: + frequency, size, channels + if (frequency, size) == (22050, 16): + continue + mixer.init(frequency, size, channels) + + mixer_conf = mixer.get_init() + + self.assertEqual(init_conf, mixer_conf) + mixer.quit() + + def test_get_init__returns_None_if_mixer_not_initialized(self): + self.assertIsNone(mixer.get_init()) + + def test_get_num_channels__defaults_eight_after_init(self): + mixer.init() + self.assertEqual(mixer.get_num_channels(), 8) + + def test_set_num_channels(self): + mixer.init() + + default_num_channels = mixer.get_num_channels() + for i in range(1, default_num_channels + 1): + mixer.set_num_channels(i) + self.assertEqual(mixer.get_num_channels(), i) + + def test_quit(self): + """ get_num_channels() Should throw pygame.error if uninitialized + after mixer.quit() """ + mixer.init() + mixer.quit() + self.assertRaises(pygame.error, mixer.get_num_channels) + + # TODO: FIXME: appveyor fails here sometimes. + @unittest.expectedFailure + def test_sound_args(self): + def get_bytes(snd): + return snd.get_raw() + + mixer.init() + + sample = as_bytes("\x00\xff") * 24 + wave_path = example_path(os.path.join("data", "house_lo.wav")) + uwave_path = unicode_(wave_path) + bwave_path = uwave_path.encode(sys.getfilesystemencoding()) + snd = mixer.Sound(file=wave_path) + self.assertTrue(snd.get_length() > 0.5) + snd_bytes = get_bytes(snd) + self.assertTrue(len(snd_bytes) > 1000) + + self.assertEqual(get_bytes(mixer.Sound(wave_path)), snd_bytes) + + self.assertEqual(get_bytes(mixer.Sound(file=uwave_path)), snd_bytes) + self.assertEqual(get_bytes(mixer.Sound(uwave_path)), snd_bytes) + arg_emsg = "Sound takes either 1 positional or 1 keyword argument" + + with self.assertRaises(TypeError) as cm: + mixer.Sound() + self.assertEqual(str(cm.exception), arg_emsg) + with self.assertRaises(TypeError) as cm: + mixer.Sound(wave_path, buffer=sample) + self.assertEqual(str(cm.exception), arg_emsg) + with self.assertRaises(TypeError) as cm: + mixer.Sound(sample, file=wave_path) + self.assertEqual(str(cm.exception), arg_emsg) + with self.assertRaises(TypeError) as cm: + mixer.Sound(buffer=sample, file=wave_path) + self.assertEqual(str(cm.exception), arg_emsg) + + with self.assertRaises(TypeError) as cm: + mixer.Sound(foobar=sample) + self.assertEqual(str(cm.exception), "Unrecognized keyword argument 'foobar'") + + snd = mixer.Sound(wave_path, **{}) + self.assertEqual(get_bytes(snd), snd_bytes) + snd = mixer.Sound(*[], **{"file": wave_path}) + + with self.assertRaises(TypeError) as cm: + mixer.Sound([]) + self.assertEqual(str(cm.exception), "Unrecognized argument (type list)") + + with self.assertRaises(TypeError) as cm: + snd = mixer.Sound(buffer=[]) + emsg = "Expected object with buffer interface: got a list" + self.assertEqual(str(cm.exception), emsg) + + ufake_path = unicode_("12345678") + self.assertRaises(IOError, mixer.Sound, ufake_path) + self.assertRaises(IOError, mixer.Sound, "12345678") + + with self.assertRaises(TypeError) as cm: + mixer.Sound(buffer=unicode_("something")) + emsg = "Unicode object not allowed as buffer object" + self.assertEqual(str(cm.exception), emsg) + self.assertEqual(get_bytes(mixer.Sound(buffer=sample)), sample) + if type(sample) != str: + somebytes = get_bytes(mixer.Sound(sample)) + # on python 2 we do not allow using string except as file name. + self.assertEqual(somebytes, sample) + self.assertEqual(get_bytes(mixer.Sound(file=bwave_path)), snd_bytes) + self.assertEqual(get_bytes(mixer.Sound(bwave_path)), snd_bytes) + + snd = mixer.Sound(wave_path) + with self.assertRaises(TypeError) as cm: + mixer.Sound(wave_path, array=snd) + self.assertEqual(str(cm.exception), arg_emsg) + with self.assertRaises(TypeError) as cm: + mixer.Sound(buffer=sample, array=snd) + self.assertEqual(str(cm.exception), arg_emsg) + snd2 = mixer.Sound(array=snd) + self.assertEqual(snd.get_raw(), snd2.get_raw()) + + def test_sound_unicode(self): + """test non-ASCII unicode path""" + mixer.init() + import shutil + + ep = unicode_(example_path("data")) + temp_file = os.path.join(ep, u"你好.wav") + org_file = os.path.join(ep, u"house_lo.wav") + shutil.copy(org_file, temp_file) + try: + with open(temp_file, "rb") as f: + pass + except IOError: + raise unittest.SkipTest("the path cannot be opened") + + try: + sound = mixer.Sound(temp_file) + del sound + finally: + os.remove(temp_file) + + @unittest.skipIf( + os.environ.get("SDL_AUDIODRIVER") == "disk", + "this test fails without real sound card", + ) + def test_array_keyword(self): + try: + from numpy import ( + array, + arange, + zeros, + int8, + uint8, + int16, + uint16, + int32, + uint32, + ) + except ImportError: + self.skipTest("requires numpy") + + freq = 22050 + format_list = [-8, 8, -16, 16] + channels_list = [1, 2] + + a_lists = dict((f, []) for f in format_list) + a32u_mono = arange(0, 256, 1, uint32) + a16u_mono = a32u_mono.astype(uint16) + a8u_mono = a32u_mono.astype(uint8) + au_list_mono = [(1, a) for a in [a8u_mono, a16u_mono, a32u_mono]] + for format in format_list: + if format > 0: + a_lists[format].extend(au_list_mono) + a32s_mono = arange(-128, 128, 1, int32) + a16s_mono = a32s_mono.astype(int16) + a8s_mono = a32s_mono.astype(int8) + as_list_mono = [(1, a) for a in [a8s_mono, a16s_mono, a32s_mono]] + for format in format_list: + if format < 0: + a_lists[format].extend(as_list_mono) + a32u_stereo = zeros([a32u_mono.shape[0], 2], uint32) + a32u_stereo[:, 0] = a32u_mono + a32u_stereo[:, 1] = 255 - a32u_mono + a16u_stereo = a32u_stereo.astype(uint16) + a8u_stereo = a32u_stereo.astype(uint8) + au_list_stereo = [(2, a) for a in [a8u_stereo, a16u_stereo, a32u_stereo]] + for format in format_list: + if format > 0: + a_lists[format].extend(au_list_stereo) + a32s_stereo = zeros([a32s_mono.shape[0], 2], int32) + a32s_stereo[:, 0] = a32s_mono + a32s_stereo[:, 1] = -1 - a32s_mono + a16s_stereo = a32s_stereo.astype(int16) + a8s_stereo = a32s_stereo.astype(int8) + as_list_stereo = [(2, a) for a in [a8s_stereo, a16s_stereo, a32s_stereo]] + for format in format_list: + if format < 0: + a_lists[format].extend(as_list_stereo) + + for format in format_list: + for channels in channels_list: + try: + mixer.init(freq, format, channels) + except pygame.error: + # Some formats (e.g. 16) may not be supported. + continue + try: + __, f, c = mixer.get_init() + if f != format or c != channels: + # Some formats (e.g. -8) may not be supported. + continue + for c, a in a_lists[format]: + self._test_array_argument(format, a, c == channels) + finally: + mixer.quit() + + def _test_array_argument(self, format, a, test_pass): + from numpy import array, all as all_ + + try: + snd = mixer.Sound(array=a) + except ValueError: + if not test_pass: + return + self.fail("Raised ValueError: Format %i, dtype %s" % (format, a.dtype)) + if not test_pass: + self.fail( + "Did not raise ValueError: Format %i, dtype %s" % (format, a.dtype) + ) + a2 = array(snd) + a3 = a.astype(a2.dtype) + lshift = abs(format) - 8 * a.itemsize + if lshift >= 0: + # This is asymmetric with respect to downcasting. + a3 <<= lshift + self.assertTrue(all_(a2 == a3), "Format %i, dtype %s" % (format, a.dtype)) + + def _test_array_interface_fail(self, a): + self.assertRaises(ValueError, mixer.Sound, array=a) + + def test_array_interface(self): + mixer.init(22050, -16, 1, allowedchanges=0) + snd = mixer.Sound(buffer=as_bytes("\x00\x7f") * 20) + d = snd.__array_interface__ + self.assertTrue(isinstance(d, dict)) + if pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN: + typestr = "") if is_lil_endian else (">", "<") + shape = (10, channels)[:ndim] + strides = (channels * itemsize, itemsize)[2 - ndim :] + exp = Exporter(shape, format=frev + "i") + snd = mixer.Sound(array=exp) + buflen = len(exp) * itemsize * channels + imp = Importer(snd, buftools.PyBUF_SIMPLE) + self.assertEqual(imp.ndim, 0) + self.assertTrue(imp.format is None) + self.assertEqual(imp.len, buflen) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.shape is None) + self.assertTrue(imp.strides is None) + self.assertTrue(imp.suboffsets is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.buf, snd._samples_address) + imp = Importer(snd, buftools.PyBUF_WRITABLE) + self.assertEqual(imp.ndim, 0) + self.assertTrue(imp.format is None) + self.assertEqual(imp.len, buflen) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.shape is None) + self.assertTrue(imp.strides is None) + self.assertTrue(imp.suboffsets is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.buf, snd._samples_address) + imp = Importer(snd, buftools.PyBUF_FORMAT) + self.assertEqual(imp.ndim, 0) + self.assertEqual(imp.format, format) + self.assertEqual(imp.len, buflen) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.shape is None) + self.assertTrue(imp.strides is None) + self.assertTrue(imp.suboffsets is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.buf, snd._samples_address) + imp = Importer(snd, buftools.PyBUF_ND) + self.assertEqual(imp.ndim, ndim) + self.assertTrue(imp.format is None) + self.assertEqual(imp.len, buflen) + self.assertEqual(imp.itemsize, itemsize) + self.assertEqual(imp.shape, shape) + self.assertTrue(imp.strides is None) + self.assertTrue(imp.suboffsets is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.buf, snd._samples_address) + imp = Importer(snd, buftools.PyBUF_STRIDES) + self.assertEqual(imp.ndim, ndim) + self.assertTrue(imp.format is None) + self.assertEqual(imp.len, buflen) + self.assertEqual(imp.itemsize, itemsize) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertTrue(imp.suboffsets is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.buf, snd._samples_address) + imp = Importer(snd, buftools.PyBUF_FULL_RO) + self.assertEqual(imp.ndim, ndim) + self.assertEqual(imp.format, format) + self.assertEqual(imp.len, buflen) + self.assertEqual(imp.itemsize, 2) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertTrue(imp.suboffsets is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.buf, snd._samples_address) + imp = Importer(snd, buftools.PyBUF_FULL_RO) + self.assertEqual(imp.ndim, ndim) + self.assertEqual(imp.format, format) + self.assertEqual(imp.len, buflen) + self.assertEqual(imp.itemsize, itemsize) + self.assertEqual(imp.shape, exp.shape) + self.assertEqual(imp.strides, strides) + self.assertTrue(imp.suboffsets is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.buf, snd._samples_address) + imp = Importer(snd, buftools.PyBUF_C_CONTIGUOUS) + self.assertEqual(imp.ndim, ndim) + self.assertTrue(imp.format is None) + self.assertEqual(imp.strides, strides) + imp = Importer(snd, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertEqual(imp.ndim, ndim) + self.assertTrue(imp.format is None) + self.assertEqual(imp.strides, strides) + if ndim == 1: + imp = Importer(snd, buftools.PyBUF_F_CONTIGUOUS) + self.assertEqual(imp.ndim, 1) + self.assertTrue(imp.format is None) + self.assertEqual(imp.strides, strides) + else: + self.assertRaises(BufferError, Importer, snd, buftools.PyBUF_F_CONTIGUOUS) + + def todo_test_fadeout(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.fadeout: + + # pygame.mixer.fadeout(time): return None + # fade out the volume on all sounds before stopping + # + # This will fade out the volume on all active channels over the time + # argument in milliseconds. After the sound is muted the playback will + # stop. + # + + self.fail() + + def todo_test_find_channel(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.find_channel: + + # pygame.mixer.find_channel(force=False): return Channel + # find an unused channel + # + # This will find and return an inactive Channel object. If there are + # no inactive Channels this function will return None. If there are no + # inactive channels and the force argument is True, this will find the + # Channel with the longest running Sound and return it. + # + # If the mixer has reserved channels from pygame.mixer.set_reserved() + # then those channels will not be returned here. + # + + self.fail() + + def todo_test_get_busy(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.get_busy: + + # pygame.mixer.get_busy(): return bool + # test if any sound is being mixed + # + # Returns True if the mixer is busy mixing any channels. If the mixer + # is idle then this return False. + # + + self.fail() + + def todo_test_pause(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.pause: + + # pygame.mixer.pause(): return None + # temporarily stop playback of all sound channels + # + # This will temporarily stop all playback on the active mixer + # channels. The playback can later be resumed with + # pygame.mixer.unpause() + # + + self.fail() + + def todo_test_set_reserved(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.set_reserved: + + # pygame.mixer.set_reserved(count): return None + # reserve channels from being automatically used + # + # The mixer can reserve any number of channels that will not be + # automatically selected for playback by Sounds. If sounds are + # currently playing on the reserved channels they will not be stopped. + # + # This allows the application to reserve a specific number of channels + # for important sounds that must not be dropped or have a guaranteed + # channel to play on. + # + + self.fail() + + def todo_test_stop(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.stop: + + # pygame.mixer.stop(): return None + # stop playback of all sound channels + # + # This will stop all playback of all active mixer channels. + + self.fail() + + def todo_test_unpause(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.unpause: + + # pygame.mixer.unpause(): return None + # resume paused playback of sound channels + # + # This will resume all active sound channels after they have been paused. + + self.fail() + + def test_get_sdl_mixer_version(self): + """Ensures get_sdl_mixer_version works correctly with no args.""" + expected_length = 3 + expected_type = tuple + expected_item_type = int + + version = pygame.mixer.get_sdl_mixer_version() + + self.assertIsInstance(version, expected_type) + self.assertEqual(len(version), expected_length) + + for item in version: + self.assertIsInstance(item, expected_item_type) + + def test_get_sdl_mixer_version__args(self): + """Ensures get_sdl_mixer_version works correctly using args.""" + expected_length = 3 + expected_type = tuple + expected_item_type = int + + for value in (True, False): + version = pygame.mixer.get_sdl_mixer_version(value) + + self.assertIsInstance(version, expected_type) + self.assertEqual(len(version), expected_length) + + for item in version: + self.assertIsInstance(item, expected_item_type) + + def test_get_sdl_mixer_version__kwargs(self): + """Ensures get_sdl_mixer_version works correctly using kwargs.""" + expected_length = 3 + expected_type = tuple + expected_item_type = int + + for value in (True, False): + version = pygame.mixer.get_sdl_mixer_version(linked=value) + + self.assertIsInstance(version, expected_type) + self.assertEqual(len(version), expected_length) + + for item in version: + self.assertIsInstance(item, expected_item_type) + + def test_get_sdl_mixer_version__invalid_args_kwargs(self): + """Ensures get_sdl_mixer_version handles invalid args and kwargs.""" + invalid_bool = InvalidBool() + + with self.assertRaises(TypeError): + version = pygame.mixer.get_sdl_mixer_version(invalid_bool) + + with self.assertRaises(TypeError): + version = pygame.mixer.get_sdl_mixer_version(linked=invalid_bool) + + def test_get_sdl_mixer_version__linked_equals_compiled(self): + """Ensures get_sdl_mixer_version's linked/compiled versions are equal. + """ + linked_version = pygame.mixer.get_sdl_mixer_version(linked=True) + complied_version = pygame.mixer.get_sdl_mixer_version(linked=False) + + self.assertTupleEqual(linked_version, complied_version) + + +############################## CHANNEL CLASS TESTS ############################# + + +class ChannelTypeTest(AssertRaisesRegexMixin, unittest.TestCase): + @classmethod + def setUpClass(cls): + # Initializing the mixer is slow, so minimize the times it is called. + mixer.init() + + @classmethod + def tearDownClass(cls): + mixer.quit() + + def setUp(cls): + # This makes sure the mixer is always initialized before each test (in + # case a test calls pygame.mixer.quit()). + if mixer.get_init() is None: + mixer.init() + + def test_channel(self): + """Ensure Channel() creation works.""" + channel = mixer.Channel(0) + + self.assertIsInstance(channel, mixer.ChannelType) + self.assertEqual(channel.__class__.__name__, "Channel") + + def test_channel__without_arg(self): + """Ensure exception for Channel() creation with no argument.""" + with self.assertRaises(TypeError): + mixer.Channel() + + def test_channel__invalid_id(self): + """Ensure exception for Channel() creation with an invalid id.""" + with self.assertRaises(IndexError): + mixer.Channel(-1) + + def test_channel__before_init(self): + """Ensure exception for Channel() creation with non-init mixer.""" + mixer.quit() + + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + mixer.Channel(0) + + def todo_test_fadeout(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.fadeout: + + # Channel.fadeout(time): return None + # stop playback after fading channel out + # + # Stop playback of a channel after fading out the sound over the given + # time argument in milliseconds. + # + + self.fail() + + def test_get_busy(self): + """Ensure an idle channel's busy state is correct.""" + expected_busy = False + channel = mixer.Channel(0) + + busy = channel.get_busy() + + self.assertEqual(busy, expected_busy) + + def todo_test_get_busy__active(self): + """Ensure an active channel's busy state is correct.""" + self.fail() + + def todo_test_get_endevent(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.get_endevent: + + # Channel.get_endevent(): return type + # get the event a channel sends when playback stops + # + # Returns the event type to be sent every time the Channel finishes + # playback of a Sound. If there is no endevent the function returns + # pygame.NOEVENT. + # + + self.fail() + + def todo_test_get_queue(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.get_queue: + + # Channel.get_queue(): return Sound + # return any Sound that is queued + # + # If a Sound is already queued on this channel it will be returned. + # Once the queued sound begins playback it will no longer be on the + # queue. + # + + self.fail() + + def todo_test_get_sound(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.get_sound: + + # Channel.get_sound(): return Sound + # get the currently playing Sound + # + # Return the actual Sound object currently playing on this channel. If + # the channel is idle None is returned. + # + + self.fail() + + def test_get_volume(self): + """Ensure a channel's volume can be retrieved.""" + expected_volume = 1.0 # default + channel = mixer.Channel(0) + + volume = channel.get_volume() + + self.assertAlmostEqual(volume, expected_volume) + + def todo_test_get_volume__while_playing(self): + """Ensure a channel's volume can be retrieved while playing.""" + self.fail() + + def todo_test_pause(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.pause: + + # Channel.pause(): return None + # temporarily stop playback of a channel + # + # Temporarily stop the playback of sound on a channel. It can be + # resumed at a later time with Channel.unpause() + # + + self.fail() + + def todo_test_play(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.play: + + # Channel.play(Sound, loops=0, maxtime=0, fade_ms=0): return None + # play a Sound on a specific Channel + # + # This will begin playback of a Sound on a specific Channel. If the + # Channel is currently playing any other Sound it will be stopped. + # + # The loops argument has the same meaning as in Sound.play(): it is + # the number of times to repeat the sound after the first time. If it + # is 3, the sound will be played 4 times (the first time, then three + # more). If loops is -1 then the playback will repeat indefinitely. + # + # As in Sound.play(), the maxtime argument can be used to stop + # playback of the Sound after a given number of milliseconds. + # + # As in Sound.play(), the fade_ms argument can be used fade in the sound. + + self.fail() + + def todo_test_queue(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.queue: + + # Channel.queue(Sound): return None + # queue a Sound object to follow the current + # + # When a Sound is queued on a Channel, it will begin playing + # immediately after the current Sound is finished. Each channel can + # only have a single Sound queued at a time. The queued Sound will + # only play if the current playback finished automatically. It is + # cleared on any other call to Channel.stop() or Channel.play(). + # + # If there is no sound actively playing on the Channel then the Sound + # will begin playing immediately. + # + + self.fail() + + def todo_test_set_endevent(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.set_endevent: + + # Channel.set_endevent(): return None + # Channel.set_endevent(type): return None + # have the channel send an event when playback stops + # + # When an endevent is set for a channel, it will send an event to the + # pygame queue every time a sound finishes playing on that channel + # (not just the first time). Use pygame.event.get() to retrieve the + # endevent once it's sent. + # + # Note that if you called Sound.play(n) or Channel.play(sound,n), the + # end event is sent only once: after the sound has been played "n+1" + # times (see the documentation of Sound.play). + # + # If Channel.stop() or Channel.play() is called while the sound was + # still playing, the event will be posted immediately. + # + # The type argument will be the event id sent to the queue. This can + # be any valid event type, but a good choice would be a value between + # pygame.locals.USEREVENT and pygame.locals.NUMEVENTS. If no type + # argument is given then the Channel will stop sending endevents. + # + + self.fail() + + def todo_test_set_volume(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.set_volume: + + # Channel.set_volume(value): return None + # Channel.set_volume(left, right): return None + # set the volume of a playing channel + # + # Set the volume (loudness) of a playing sound. When a channel starts + # to play its volume value is reset. This only affects the current + # sound. The value argument is between 0.0 and 1.0. + # + # If one argument is passed, it will be the volume of both speakers. + # If two arguments are passed and the mixer is in stereo mode, the + # first argument will be the volume of the left speaker and the second + # will be the volume of the right speaker. (If the second argument is + # None, the first argument will be the volume of both speakers.) + # + # If the channel is playing a Sound on which set_volume() has also + # been called, both calls are taken into account. For example: + # + # sound = pygame.mixer.Sound("s.wav") + # channel = s.play() # Sound plays at full volume by default + # sound.set_volume(0.9) # Now plays at 90% of full volume. + # sound.set_volume(0.6) # Now plays at 60% (previous value replaced). + # channel.set_volume(0.5) # Now plays at 30% (0.6 * 0.5). + + self.fail() + + def todo_test_stop(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.stop: + + # Channel.stop(): return None + # stop playback on a Channel + # + # Stop sound playback on a channel. After playback is stopped the + # channel becomes available for new Sounds to play on it. + # + + self.fail() + + def todo_test_unpause(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.unpause: + + # Channel.unpause(): return None + # resume pause playback of a channel + # + # Resume the playback on a paused channel. + + self.fail() + + +############################### SOUND CLASS TESTS ############################## + + +class SoundTypeTest(AssertRaisesRegexMixin, unittest.TestCase): + @classmethod + def setUpClass(cls): + # Initializing the mixer is slow, so minimize the times it is called. + mixer.init() + + @classmethod + def tearDownClass(cls): + mixer.quit() + + def setUp(cls): + # This makes sure the mixer is always initialized before each test (in + # case a test calls pygame.mixer.quit()). + if mixer.get_init() is None: + mixer.init() + + # See MixerModuleTest's methods test_sound_args(), test_sound_unicode(), + # and test_array_keyword() for additional testing of Sound() creation. + def test_sound(self): + """Ensure Sound() creation with a filename works.""" + filename = example_path(os.path.join("data", "house_lo.wav")) + sound1 = mixer.Sound(filename) + sound2 = mixer.Sound(file=filename) + + self.assertIsInstance(sound1, mixer.Sound) + self.assertIsInstance(sound2, mixer.Sound) + + def test_sound__from_file_object(self): + """Ensure Sound() creation with a file object works.""" + filename = example_path(os.path.join("data", "house_lo.wav")) + + # Using 'with' ensures the file is closed even if test fails. + with open(filename, "rb") as file_obj: + sound = mixer.Sound(file_obj) + + self.assertIsInstance(sound, mixer.Sound) + + def test_sound__from_sound_object(self): + """Ensure Sound() creation with a Sound() object works.""" + filename = example_path(os.path.join("data", "house_lo.wav")) + sound_obj = mixer.Sound(file=filename) + + sound = mixer.Sound(sound_obj) + + self.assertIsInstance(sound, mixer.Sound) + + def todo_test_sound__from_buffer(self): + """Ensure Sound() creation with a buffer works.""" + self.fail() + + def todo_test_sound__from_array(self): + """Ensure Sound() creation with an array works.""" + self.fail() + + def test_sound__without_arg(self): + """Ensure exception raised for Sound() creation with no argument.""" + with self.assertRaises(TypeError): + mixer.Sound() + + def test_sound__before_init(self): + """Ensure exception raised for Sound() creation with non-init mixer.""" + mixer.quit() + filename = example_path(os.path.join("data", "house_lo.wav")) + + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + mixer.Sound(file=filename) + + @unittest.skipIf(IS_PYPY, "pypy skip") + def test_samples_address(self): + """Test the _samples_address getter.""" + from ctypes import pythonapi, c_void_p, py_object + + try: + Bytes_FromString = pythonapi.PyBytes_FromString # python 3 + except: + Bytes_FromString = pythonapi.PyString_FromString # python 2 + + Bytes_FromString.restype = c_void_p + Bytes_FromString.argtypes = [py_object] + samples = as_bytes("abcdefgh") # keep byte size a multiple of 4 + sample_bytes = Bytes_FromString(samples) + + snd = mixer.Sound(buffer=samples) + + self.assertNotEqual(snd._samples_address, sample_bytes) + + def todo_test_fadeout(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.Sound.fadeout: + + # Sound.fadeout(time): return None + # stop sound playback after fading out + # + # This will stop playback of the sound after fading it out over the + # time argument in milliseconds. The Sound will fade and stop on all + # actively playing channels. + # + + self.fail() + + def todo_test_get_length(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.Sound.get_length: + + # Sound.get_length(): return seconds + # get the length of the Sound + # + # Return the length of this Sound in seconds. + + self.fail() + + def test_get_num_channels(self): + """Ensure correct number of channels.""" + expected_channels = 0 + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) + + num_channels = sound.get_num_channels() + + self.assertEqual(num_channels, expected_channels) + + def todo_test_get_num_channels__while_playing(self): + """Ensure correct number of channels while playing.""" + self.fail() + + def test_get_volume(self): + """Ensure a sound's volume can be retrieved.""" + expected_volume = 1.0 # default + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) + + volume = sound.get_volume() + + self.assertAlmostEqual(volume, expected_volume) + + def todo_test_get_volume__while_playing(self): + """Ensure a sound's volume can be retrieved while playing.""" + self.fail() + + def todo_test_play(self): + + # __doc__ (as of 2008-08-02) for pygame.mixer.Sound.play: + + # Sound.play(loops=0, maxtime=0, fade_ms=0): return Channel + # begin sound playback + # + # Begin playback of the Sound (i.e., on the computer's speakers) on an + # available Channel. This will forcibly select a Channel, so playback + # may cut off a currently playing sound if necessary. + # + # The loops argument controls how many times the sample will be + # repeated after being played the first time. A value of 5 means that + # the sound will be played once, then repeated five times, and so is + # played a total of six times. The default value (zero) means the + # Sound is not repeated, and so is only played once. If loops is set + # to -1 the Sound will loop indefinitely (though you can still call + # stop() to stop it). + # + # The maxtime argument can be used to stop playback after a given + # number of milliseconds. + # + # The fade_ms argument will make the sound start playing at 0 volume + # and fade up to full volume over the time given. The sample may end + # before the fade-in is complete. + # + # This returns the Channel object for the channel that was selected. + + self.fail() + + def test_set_volume(self): + """Ensure a sound's volume can be set.""" + float_delta = 1.0 / 128 # SDL volume range is 0 to 128 + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) + current_volume = sound.get_volume() + + # (volume_set_value : expected_volume) + volumes = ( + (-1, current_volume), # value < 0 won't change volume + (0, 0.0), + (0.01, 0.01), + (0.1, 0.1), + (0.5, 0.5), + (0.9, 0.9), + (0.99, 0.99), + (1, 1.0), + (1.1, 1.0), + (2.0, 1.0), + ) + + for volume_set_value, expected_volume in volumes: + sound.set_volume(volume_set_value) + + self.assertAlmostEqual( + sound.get_volume(), expected_volume, delta=float_delta + ) + + def todo_test_set_volume__while_playing(self): + """Ensure a sound's volume can be set while playing.""" + self.fail() + + def test_stop(self): + """Ensure stop can be called while not playing a sound.""" + expected_channels = 0 + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) + + sound.stop() + + self.assertEqual(sound.get_num_channels(), expected_channels) + + def todo_test_stop__while_playing(self): + """Ensure stop stops a playing sound.""" + self.fail() + + def test_get_raw(self): + """Ensure get_raw returns the correct bytestring.""" + samples = as_bytes("abcdefgh") # keep byte size a multiple of 4 + snd = mixer.Sound(buffer=samples) + + raw = snd.get_raw() + + self.assertIsInstance(raw, bytes_) + self.assertEqual(raw, samples) + + +##################################### MAIN ##################################### + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/mouse_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/mouse_test.py new file mode 100644 index 0000000..ddc06c1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/mouse_test.py @@ -0,0 +1,103 @@ +import unittest + +import pygame + + +class MouseModuleTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + # The display needs to be initialized for mouse functions. + pygame.display.init() + + @classmethod + def tearDownClass(cls): + pygame.display.quit() + + def todo_test_get_cursor(self): + """Ensures get_cursor works correctly.""" + self.fail() + + def todo_test_set_cursor(self): + """Ensures set_cursor works correctly.""" + self.fail() + + def test_get_focused(self): + """Ensures get_focused returns the correct type.""" + focused = pygame.mouse.get_focused() + + self.assertIsInstance(focused, int) + + def test_get_pressed(self): + """Ensures get_pressed returns the correct types.""" + expected_length = 3 + + buttons_pressed = pygame.mouse.get_pressed() + + self.assertIsInstance(buttons_pressed, tuple) + self.assertEqual(len(buttons_pressed), expected_length) + for value in buttons_pressed: + self.assertIsInstance(value, int) + + def test_get_pos(self): + """Ensures get_pos returns the correct types.""" + expected_length = 2 + + pos = pygame.mouse.get_pos() + + self.assertIsInstance(pos, tuple) + self.assertEqual(len(pos), expected_length) + for value in pos: + self.assertIsInstance(value, int) + + def test_set_pos__invalid_pos(self): + """Ensures set_pos handles invalid positions correctly.""" + for invalid_pos in ((1,), [1, 2, 3], 1, "1", (1, "1"), []): + + with self.assertRaises(TypeError): + pygame.mouse.set_pos(invalid_pos) + + def todo_test_set_pos(self): + """Ensures set_pos works correctly.""" + self.fail() + + def test_get_rel(self): + """Ensures get_rel returns the correct types.""" + expected_length = 2 + + rel = pygame.mouse.get_rel() + + self.assertIsInstance(rel, tuple) + self.assertEqual(len(rel), expected_length) + for value in rel: + self.assertIsInstance(value, int) + + def test_get_visible(self): + """Ensures get_visible works correctly.""" + for expected_value in (False, True): + pygame.mouse.set_visible(expected_value) + + visible = pygame.mouse.get_visible() + + self.assertEqual(visible, expected_value) + + def test_set_visible(self): + """Ensures set_visible returns the correct values.""" + # Set to a known state. + pygame.mouse.set_visible(True) + + for expected_visible in (False, True): + prev_visible = pygame.mouse.set_visible(expected_visible) + + self.assertEqual(prev_visible, not expected_visible) + + def test_set_visible__invalid_value(self): + """Ensures set_visible handles invalid positions correctly.""" + for invalid_value in ((1,), [1, 2, 3], 1.1, "1", (1, "1"), []): + with self.assertRaises(TypeError): + prev_visible = pygame.mouse.set_visible(invalid_value) + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/overlay_tags.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/overlay_tags.py new file mode 100644 index 0000000..ea739ad --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/overlay_tags.py @@ -0,0 +1,2 @@ +# Overlay support was removed in SDL 2 +__tags__ = ["SDL2_ignore"] diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/overlay_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/overlay_test.py new file mode 100644 index 0000000..c7d5db2 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/overlay_test.py @@ -0,0 +1,37 @@ +import unittest + + +class OverlayTypeTest(unittest.TestCase): + def todo_test_display(self): + + # __doc__ (as of 2008-08-02) for pygame.overlay.overlay.display: + + # Overlay.display((y, u, v)): return None + # Overlay.display(): return None + # set the overlay pixel data + + self.fail() + + def todo_test_get_hardware(self): + + # __doc__ (as of 2008-08-02) for pygame.overlay.overlay.get_hardware: + + # Overlay.get_hardware(rect): return int + # test if the Overlay is hardware accelerated + + self.fail() + + def todo_test_set_location(self): + + # __doc__ (as of 2008-08-02) for pygame.overlay.overlay.set_location: + + # Overlay.set_location(rect): return None + # control where the overlay is displayed + + self.fail() + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/pixelarray_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/pixelarray_test.py new file mode 100644 index 0000000..0e94bf4 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/pixelarray_test.py @@ -0,0 +1,1636 @@ +import sys +import platform + +try: + reduce +except NameError: + from functools import reduce +import operator +import weakref +import gc +import unittest + +from pygame.tests.test_utils import SurfaceSubclass + +try: + from pygame.tests.test_utils import arrinter +except NameError: + pass + +import pygame +from pygame.compat import xrange_ + +PY3 = sys.version_info >= (3, 0, 0) +IS_PYPY = "PyPy" == platform.python_implementation() + + +class TestMixin(object): + def assert_surfaces_equal(self, s1, s2, msg=None): + """Checks if two surfaces are equal in size and color.""" + w, h = s1.get_size() + + self.assertTupleEqual((w, h), s2.get_size(), msg) + + msg = "" if msg is None else "{}, ".format(msg) + msg += "size: ({}, {})".format(w, h) + + for x in range(w): + for y in range(h): + self.assertEqual( + s1.get_at((x, y)), + s2.get_at((x, y)), + "{}, position: ({}, {})".format(msg, x, y), + ) + + def assert_surface_filled(self, surface, expected_color, msg=None): + """Checks if the surface is filled with the given color.""" + width, height = surface.get_size() + + surface.lock() # Lock for possible speed up. + for pos in ((x, y) for y in range(height) for x in range(width)): + self.assertEqual(surface.get_at(pos), expected_color, msg) + surface.unlock() + + +class PixelArrayTypeTest(unittest.TestCase, TestMixin): + def test_compare(self): + # __doc__ (as of 2008-06-25) for pygame.pixelarray.PixelArray.compare: + + # PixelArray.compare (array, distance=0, weights=(0.299, 0.587, 0.114)): Return PixelArray + # Compares the PixelArray with another one. + + w = 10 + h = 20 + size = w, h + sf = pygame.Surface(size, 0, 32) + ar = pygame.PixelArray(sf) + sf2 = pygame.Surface(size, 0, 32) + self.assertRaises(TypeError, ar.compare, sf2) + ar2 = pygame.PixelArray(sf2) + ar3 = ar.compare(ar2) + self.assertTrue(isinstance(ar3, pygame.PixelArray)) + self.assertEqual(ar3.shape, size) + sf2.fill(pygame.Color("white")) + self.assert_surfaces_equal(sf2, ar3.surface) + del ar3 + r = pygame.Rect(2, 5, 6, 13) + sf.fill(pygame.Color("blue"), r) + sf2.fill(pygame.Color("red")) + sf2.fill(pygame.Color("blue"), r) + ar3 = ar.compare(ar2) + sf.fill(pygame.Color("white"), r) + self.assert_surfaces_equal(sf, ar3.surface) + + # FINISH ME! + # Test other bit depths, slices, and distance != 0. + + def test_compare__same_colors_within_distance(self): + """Ensures compare works correctly with same colored surfaces.""" + size = (3, 5) + pixelarray_result_color = pygame.Color("white") + surface_color = (127, 127, 127, 255) + + for depth in (8, 16, 24, 32): + expected_pixelarray_surface = pygame.Surface(size, depth=depth) + expected_pixelarray_surface.fill(pixelarray_result_color) + + # Copy the surface to ensure same dimensions/formatting. + surf_a = expected_pixelarray_surface.copy() + surf_a.fill(surface_color) + # For non-32 bit depths, the actual color can be different from what + # was filled. + expected_surface_color = surf_a.get_at((0, 0)) + + pixelarray_a = pygame.PixelArray(surf_a) + pixelarray_b = pygame.PixelArray(surf_a.copy()) + + for distance in (0.0, 0.01, 0.1, 1.0): + pixelarray_result = pixelarray_a.compare( + pixelarray_b, distance=distance + ) + + # Ensure the resulting pixelarray is correct and that the original + # surfaces were not changed. + self.assert_surfaces_equal( + pixelarray_result.surface, + expected_pixelarray_surface, + (depth, distance), + ) + self.assert_surface_filled( + pixelarray_a.surface, expected_surface_color, (depth, distance) + ) + self.assert_surface_filled( + pixelarray_b.surface, expected_surface_color, (depth, distance) + ) + + pixelarray_a.close() + pixelarray_b.close() + pixelarray_result.close() + + def test_compare__different_colors_within_distance(self): + """Ensures compare works correctly with different colored surfaces + and the color difference is within the given distance. + """ + size = (3, 5) + pixelarray_result_color = pygame.Color("white") + surface_a_color = (127, 127, 127, 255) + surface_b_color = (128, 127, 127, 255) + + for depth in (8, 16, 24, 32): + expected_pixelarray_surface = pygame.Surface(size, depth=depth) + expected_pixelarray_surface.fill(pixelarray_result_color) + + # Copy the surface to ensure same dimensions/formatting. + surf_a = expected_pixelarray_surface.copy() + surf_a.fill(surface_a_color) + # For non-32 bit depths, the actual color can be different from what + # was filled. + expected_surface_a_color = surf_a.get_at((0, 0)) + pixelarray_a = pygame.PixelArray(surf_a) + + surf_b = expected_pixelarray_surface.copy() + surf_b.fill(surface_b_color) + # For non-32 bit depths, the actual color can be different from what + # was filled. + expected_surface_b_color = surf_b.get_at((0, 0)) + pixelarray_b = pygame.PixelArray(surf_b) + + for distance in (0.2, 0.3, 0.5, 1.0): + pixelarray_result = pixelarray_a.compare( + pixelarray_b, distance=distance + ) + + # Ensure the resulting pixelarray is correct and that the original + # surfaces were not changed. + self.assert_surfaces_equal( + pixelarray_result.surface, + expected_pixelarray_surface, + (depth, distance), + ) + self.assert_surface_filled( + pixelarray_a.surface, expected_surface_a_color, (depth, distance) + ) + self.assert_surface_filled( + pixelarray_b.surface, expected_surface_b_color, (depth, distance) + ) + + pixelarray_a.close() + pixelarray_b.close() + pixelarray_result.close() + + def test_compare__different_colors_not_within_distance(self): + """Ensures compare works correctly with different colored surfaces + and the color difference is not within the given distance. + """ + size = (3, 5) + pixelarray_result_color = pygame.Color("black") + surface_a_color = (127, 127, 127, 255) + surface_b_color = (128, 127, 127, 255) + + for depth in (8, 16, 24, 32): + expected_pixelarray_surface = pygame.Surface(size, depth=depth) + expected_pixelarray_surface.fill(pixelarray_result_color) + + # Copy the surface to ensure same dimensions/formatting. + surf_a = expected_pixelarray_surface.copy() + surf_a.fill(surface_a_color) + # For non-32 bit depths, the actual color can be different from what + # was filled. + expected_surface_a_color = surf_a.get_at((0, 0)) + pixelarray_a = pygame.PixelArray(surf_a) + + surf_b = expected_pixelarray_surface.copy() + surf_b.fill(surface_b_color) + # For non-32 bit depths, the actual color can be different from what + # was filled. + expected_surface_b_color = surf_b.get_at((0, 0)) + pixelarray_b = pygame.PixelArray(surf_b) + + for distance in (0.0, 0.00001, 0.0001, 0.001): + pixelarray_result = pixelarray_a.compare( + pixelarray_b, distance=distance + ) + + # Ensure the resulting pixelarray is correct and that the original + # surfaces were not changed. + self.assert_surfaces_equal( + pixelarray_result.surface, + expected_pixelarray_surface, + (depth, distance), + ) + self.assert_surface_filled( + pixelarray_a.surface, expected_surface_a_color, (depth, distance) + ) + self.assert_surface_filled( + pixelarray_b.surface, expected_surface_b_color, (depth, distance) + ) + + pixelarray_a.close() + pixelarray_b.close() + pixelarray_result.close() + + def test_close(self): + """ does not crash when it is deleted. + """ + s = pygame.Surface((10, 10)) + a = pygame.PixelArray(s) + a.close() + del a + + def test_close_raises(self): + """ when you try to do an operation after it is closed. + """ + s = pygame.Surface((10, 10)) + a = pygame.PixelArray(s) + a.close() + + def do_operation(): + a[:] + + self.assertRaises(ValueError, do_operation) + + def do_operation2(): + a[:] = 1 + + self.assertRaises(ValueError, do_operation2) + + def do_operation3(): + a.make_surface() + + self.assertRaises(ValueError, do_operation3) + + def do_operation4(): + for x in a: + pass + + self.assertRaises(ValueError, do_operation4) + + def test_context_manager(self): + """ closes properly. + """ + s = pygame.Surface((10, 10)) + with pygame.PixelArray(s) as a: + a[:] + + def test_pixel_array(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((10, 20), 0, bpp) + sf.fill((0, 0, 0)) + ar = pygame.PixelArray(sf) + + self.assertEqual(ar._pixels_address, sf._pixels_address) + + if sf.mustlock(): + self.assertTrue(sf.get_locked()) + + self.assertEqual(len(ar), 10) + + del ar + if sf.mustlock(): + self.assertFalse(sf.get_locked()) + + def test_as_class(self): + # Check general new-style class freatures. + sf = pygame.Surface((2, 3), 0, 32) + ar = pygame.PixelArray(sf) + self.assertRaises(AttributeError, getattr, ar, "nonnative") + ar.nonnative = "value" + self.assertEqual(ar.nonnative, "value") + r = weakref.ref(ar) + self.assertTrue(r() is ar) + del ar + gc.collect() + self.assertTrue(r() is None) + + class C(pygame.PixelArray): + def __str__(self): + return "string (%i, %i)" % self.shape + + ar = C(sf) + self.assertEqual(str(ar), "string (2, 3)") + r = weakref.ref(ar) + self.assertTrue(r() is ar) + del ar + gc.collect() + self.assertTrue(r() is None) + + def test_pixelarray__subclassed_surface(self): + """Ensure the PixelArray constructor accepts subclassed surfaces.""" + surface = SurfaceSubclass((3, 5), 0, 32) + pixelarray = pygame.PixelArray(surface) + + self.assertIsInstance(pixelarray, pygame.PixelArray) + + # Sequence interfaces + def test_get_column(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((6, 8), 0, bpp) + sf.fill((0, 0, 255)) + val = sf.map_rgb((0, 0, 255)) + ar = pygame.PixelArray(sf) + + ar2 = ar.__getitem__(1) + self.assertEqual(len(ar2), 8) + self.assertEqual(ar2.__getitem__(0), val) + self.assertEqual(ar2.__getitem__(1), val) + self.assertEqual(ar2.__getitem__(2), val) + + ar2 = ar.__getitem__(-1) + self.assertEqual(len(ar2), 8) + self.assertEqual(ar2.__getitem__(0), val) + self.assertEqual(ar2.__getitem__(1), val) + self.assertEqual(ar2.__getitem__(2), val) + + def test_get_pixel(self): + w = 10 + h = 20 + size = w, h + bg_color = (0, 0, 255) + fg_color_y = (0, 0, 128) + fg_color_x = (0, 0, 11) + for bpp in (8, 16, 24, 32): + sf = pygame.Surface(size, 0, bpp) + mapped_bg_color = sf.map_rgb(bg_color) + mapped_fg_color_y = sf.map_rgb(fg_color_y) + mapped_fg_color_x = sf.map_rgb(fg_color_x) + self.assertNotEqual( + mapped_fg_color_y, + mapped_bg_color, + "Unusable test colors for bpp %i" % (bpp,), + ) + self.assertNotEqual( + mapped_fg_color_x, + mapped_bg_color, + "Unusable test colors for bpp %i" % (bpp,), + ) + self.assertNotEqual( + mapped_fg_color_y, + mapped_fg_color_x, + "Unusable test colors for bpp %i" % (bpp,), + ) + sf.fill(bg_color) + + ar = pygame.PixelArray(sf) + + ar_y = ar.__getitem__(1) + for y in xrange_(h): + ar2 = ar_y.__getitem__(y) + self.assertEqual( + ar2, + mapped_bg_color, + "ar[1][%i] == %i, mapped_bg_color == %i" + % (y, ar2, mapped_bg_color), + ) + + sf.set_at((1, y), fg_color_y) + ar2 = ar_y.__getitem__(y) + self.assertEqual( + ar2, + mapped_fg_color_y, + "ar[1][%i] == %i, mapped_fg_color_y == %i" + % (y, ar2, mapped_fg_color_y), + ) + + sf.set_at((1, 1), bg_color) + for x in xrange_(w): + ar2 = ar.__getitem__(x).__getitem__(1) + self.assertEqual( + ar2, + mapped_bg_color, + "ar[%i][1] = %i, mapped_bg_color = %i" % (x, ar2, mapped_bg_color), + ) + sf.set_at((x, 1), fg_color_x) + ar2 = ar.__getitem__(x).__getitem__(1) + self.assertEqual( + ar2, + mapped_fg_color_x, + "ar[%i][1] = %i, mapped_fg_color_x = %i" + % (x, ar2, mapped_fg_color_x), + ) + + ar2 = ar.__getitem__(0).__getitem__(0) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(1).__getitem__(0) + self.assertEqual(ar2, mapped_fg_color_y, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(-4).__getitem__(1) + self.assertEqual(ar2, mapped_fg_color_x, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(-4).__getitem__(5) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(-4).__getitem__(0) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(-w + 1).__getitem__(0) + self.assertEqual(ar2, mapped_fg_color_y, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(-w).__getitem__(0) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(5).__getitem__(-4) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(5).__getitem__(-h + 1) + self.assertEqual(ar2, mapped_fg_color_x, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(5).__getitem__(-h) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(0).__getitem__(-h + 1) + self.assertEqual(ar2, mapped_fg_color_x, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(0).__getitem__(-h) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + def test_set_pixel(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((10, 20), 0, bpp) + sf.fill((0, 0, 0)) + ar = pygame.PixelArray(sf) + + ar.__getitem__(0).__setitem__(0, (0, 255, 0)) + self.assertEqual(ar[0][0], sf.map_rgb((0, 255, 0))) + + ar.__getitem__(1).__setitem__(1, (128, 128, 128)) + self.assertEqual(ar[1][1], sf.map_rgb((128, 128, 128))) + + ar.__getitem__(-1).__setitem__(-1, (128, 128, 128)) + self.assertEqual(ar[9][19], sf.map_rgb((128, 128, 128))) + + ar.__getitem__(-2).__setitem__(-2, (128, 128, 128)) + self.assertEqual(ar[8][-2], sf.map_rgb((128, 128, 128))) + + def test_set_column(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((6, 8), 0, bpp) + sf.fill((0, 0, 0)) + ar = pygame.PixelArray(sf) + + sf2 = pygame.Surface((6, 8), 0, bpp) + sf2.fill((0, 255, 255)) + ar2 = pygame.PixelArray(sf2) + + # Test single value assignment + ar.__setitem__(2, (128, 128, 128)) + self.assertEqual(ar[2][0], sf.map_rgb((128, 128, 128))) + self.assertEqual(ar[2][1], sf.map_rgb((128, 128, 128))) + + ar.__setitem__(-1, (0, 255, 255)) + self.assertEqual(ar[5][0], sf.map_rgb((0, 255, 255))) + self.assertEqual(ar[-1][1], sf.map_rgb((0, 255, 255))) + + ar.__setitem__(-2, (255, 255, 0)) + self.assertEqual(ar[4][0], sf.map_rgb((255, 255, 0))) + self.assertEqual(ar[-2][1], sf.map_rgb((255, 255, 0))) + + # Test list assignment. + ar.__setitem__(0, [(255, 255, 255)] * 8) + self.assertEqual(ar[0][0], sf.map_rgb((255, 255, 255))) + self.assertEqual(ar[0][1], sf.map_rgb((255, 255, 255))) + + # Test tuple assignment. + # Changed in Pygame 1.9.2 - Raises an exception. + self.assertRaises( + ValueError, + ar.__setitem__, + 1, + ( + (204, 0, 204), + (17, 17, 17), + (204, 0, 204), + (17, 17, 17), + (204, 0, 204), + (17, 17, 17), + (204, 0, 204), + (17, 17, 17), + ), + ) + + # Test pixel array assignment. + ar.__setitem__(1, ar2.__getitem__(3)) + self.assertEqual(ar[1][0], sf.map_rgb((0, 255, 255))) + self.assertEqual(ar[1][1], sf.map_rgb((0, 255, 255))) + + def test_get_slice(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((10, 20), 0, bpp) + sf.fill((0, 0, 0)) + ar = pygame.PixelArray(sf) + + self.assertEqual(len(ar[0:2]), 2) + self.assertEqual(len(ar[3:7][3]), 20) + + self.assertEqual(ar[0:0], None) + self.assertEqual(ar[5:5], None) + self.assertEqual(ar[9:9], None) + + # Has to resolve to ar[7:8] + self.assertEqual(len(ar[-3:-2]), 1) # 2D + self.assertEqual(len(ar[-3:-2][0]), 20) # 1D + + # Try assignments. + + # 2D assignment. + ar[2:5] = (255, 255, 255) + + # 1D assignment + ar[3][3:7] = (10, 10, 10) + self.assertEqual(ar[3][5], sf.map_rgb((10, 10, 10))) + self.assertEqual(ar[3][6], sf.map_rgb((10, 10, 10))) + + @unittest.skipIf(IS_PYPY, "skipping for PyPy (segfaults on mac pypy3 6.0.0)") + def test_contains(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((10, 20), 0, bpp) + sf.fill((0, 0, 0)) + sf.set_at((8, 8), (255, 255, 255)) + + ar = pygame.PixelArray(sf) + self.assertTrue((0, 0, 0) in ar) + self.assertTrue((255, 255, 255) in ar) + self.assertFalse((255, 255, 0) in ar) + self.assertFalse(0x0000FF in ar) + + # Test sliced array + self.assertTrue((0, 0, 0) in ar[8]) + self.assertTrue((255, 255, 255) in ar[8]) + self.assertFalse((255, 255, 0) in ar[8]) + self.assertFalse(0x0000FF in ar[8]) + + def test_get_surface(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((10, 20), 0, bpp) + sf.fill((0, 0, 0)) + ar = pygame.PixelArray(sf) + self.assertTrue(ar.surface is sf) + + def test_get_surface__subclassed_surface(self): + """Ensure the surface attribute can handle subclassed surfaces.""" + expected_surface = SurfaceSubclass((5, 3), 0, 32) + pixelarray = pygame.PixelArray(expected_surface) + + surface = pixelarray.surface + + self.assertIs(surface, expected_surface) + self.assertIsInstance(surface, pygame.Surface) + self.assertIsInstance(surface, SurfaceSubclass) + + def test_set_slice(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((6, 8), 0, bpp) + sf.fill((0, 0, 0)) + ar = pygame.PixelArray(sf) + + # Test single value assignment + val = sf.map_rgb((128, 128, 128)) + ar[0:2] = val + self.assertEqual(ar[0][0], val) + self.assertEqual(ar[0][1], val) + self.assertEqual(ar[1][0], val) + self.assertEqual(ar[1][1], val) + + val = sf.map_rgb((0, 255, 255)) + ar[-3:-1] = val + self.assertEqual(ar[3][0], val) + self.assertEqual(ar[-2][1], val) + + val = sf.map_rgb((255, 255, 255)) + ar[-3:] = (255, 255, 255) + self.assertEqual(ar[4][0], val) + self.assertEqual(ar[-1][1], val) + + # Test array size mismatch. + # Changed in ver. 1.9.2 + # (was "Test list assignment, this is a vertical assignment.") + val = sf.map_rgb((0, 255, 0)) + self.assertRaises(ValueError, ar.__setitem__, slice(2, 4), [val] * 8) + + # And the horizontal assignment. + val = sf.map_rgb((255, 0, 0)) + val2 = sf.map_rgb((128, 0, 255)) + ar[0:2] = [val, val2] + self.assertEqual(ar[0][0], val) + self.assertEqual(ar[1][0], val2) + self.assertEqual(ar[0][1], val) + self.assertEqual(ar[1][1], val2) + self.assertEqual(ar[0][4], val) + self.assertEqual(ar[1][4], val2) + self.assertEqual(ar[0][5], val) + self.assertEqual(ar[1][5], val2) + + # Test pixelarray assignment. + ar[:] = (0, 0, 0) + sf2 = pygame.Surface((6, 8), 0, bpp) + sf2.fill((255, 0, 255)) + + val = sf.map_rgb((255, 0, 255)) + ar2 = pygame.PixelArray(sf2) + + ar[:] = ar2[:] + self.assertEqual(ar[0][0], val) + self.assertEqual(ar[5][7], val) + + # Ensure p1 ... pn are freed for array[...] = [p1, ..., pn] + # Bug fix: reference counting. + if hasattr(sys, "getrefcount"): + + class Int(int): + """Unique int instances""" + + pass + + sf = pygame.Surface((5, 2), 0, 32) + ar = pygame.PixelArray(sf) + pixel_list = [Int(i) for i in range(ar.shape[0])] + refcnts_before = [sys.getrefcount(i) for i in pixel_list] + ar[...] = pixel_list + refcnts_after = [sys.getrefcount(i) for i in pixel_list] + gc.collect() + self.assertEqual(refcnts_after, refcnts_before) + + def test_subscript(self): + # By default we do not need to work with any special __***__ + # methods as map subscripts are the first looked up by the + # object system. + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((6, 8), 0, bpp) + sf.set_at((1, 3), (0, 255, 0)) + sf.set_at((0, 0), (0, 255, 0)) + sf.set_at((4, 4), (0, 255, 0)) + val = sf.map_rgb((0, 255, 0)) + + ar = pygame.PixelArray(sf) + + # Test single value requests. + self.assertEqual(ar[1, 3], val) + self.assertEqual(ar[0, 0], val) + self.assertEqual(ar[4, 4], val) + self.assertEqual(ar[1][3], val) + self.assertEqual(ar[0][0], val) + self.assertEqual(ar[4][4], val) + + # Test ellipse working. + self.assertEqual(len(ar[..., ...]), 6) + self.assertEqual(len(ar[1, ...]), 8) + self.assertEqual(len(ar[..., 3]), 6) + + # Test simple slicing + self.assertEqual(len(ar[:, :]), 6) + self.assertEqual(len(ar[:,]), 6) + self.assertEqual(len(ar[1, :]), 8) + self.assertEqual(len(ar[:, 2]), 6) + # Empty slices + self.assertEqual(ar[4:4,], None) + self.assertEqual(ar[4:4, ...], None) + self.assertEqual(ar[4:4, 2:2], None) + self.assertEqual(ar[4:4, 1:4], None) + self.assertEqual(ar[4:4:2,], None) + self.assertEqual(ar[4:4:-2,], None) + self.assertEqual(ar[4:4:1, ...], None) + self.assertEqual(ar[4:4:-1, ...], None) + self.assertEqual(ar[4:4:1, 2:2], None) + self.assertEqual(ar[4:4:-1, 1:4], None) + self.assertEqual(ar[..., 4:4], None) + self.assertEqual(ar[1:4, 4:4], None) + self.assertEqual(ar[..., 4:4:1], None) + self.assertEqual(ar[..., 4:4:-1], None) + self.assertEqual(ar[2:2, 4:4:1], None) + self.assertEqual(ar[1:4, 4:4:-1], None) + + # Test advanced slicing + ar[0] = 0 + ar[1] = 1 + ar[2] = 2 + ar[3] = 3 + ar[4] = 4 + ar[5] = 5 + + # We should receive something like [0,2,4] + self.assertEqual(ar[::2, 1][0], 0) + self.assertEqual(ar[::2, 1][1], 2) + self.assertEqual(ar[::2, 1][2], 4) + # We should receive something like [2,2,2] + self.assertEqual(ar[2, ::2][0], 2) + self.assertEqual(ar[2, ::2][1], 2) + self.assertEqual(ar[2, ::2][2], 2) + + # Should create a 3x3 array of [0,2,4] + ar2 = ar[::2, ::2] + self.assertEqual(len(ar2), 3) + self.assertEqual(ar2[0][0], 0) + self.assertEqual(ar2[0][1], 0) + self.assertEqual(ar2[0][2], 0) + self.assertEqual(ar2[2][0], 4) + self.assertEqual(ar2[2][1], 4) + self.assertEqual(ar2[2][2], 4) + self.assertEqual(ar2[1][0], 2) + self.assertEqual(ar2[2][0], 4) + self.assertEqual(ar2[1][1], 2) + + # Should create a reversed 3x8 array over X of [1,2,3] -> [3,2,1] + ar2 = ar[3:0:-1] + self.assertEqual(len(ar2), 3) + self.assertEqual(ar2[0][0], 3) + self.assertEqual(ar2[0][1], 3) + self.assertEqual(ar2[0][2], 3) + self.assertEqual(ar2[0][7], 3) + self.assertEqual(ar2[2][0], 1) + self.assertEqual(ar2[2][1], 1) + self.assertEqual(ar2[2][2], 1) + self.assertEqual(ar2[2][7], 1) + self.assertEqual(ar2[1][0], 2) + self.assertEqual(ar2[1][1], 2) + # Should completely reverse the array over X -> [5,4,3,2,1,0] + ar2 = ar[::-1] + self.assertEqual(len(ar2), 6) + self.assertEqual(ar2[0][0], 5) + self.assertEqual(ar2[0][1], 5) + self.assertEqual(ar2[0][3], 5) + self.assertEqual(ar2[0][-1], 5) + self.assertEqual(ar2[1][0], 4) + self.assertEqual(ar2[1][1], 4) + self.assertEqual(ar2[1][3], 4) + self.assertEqual(ar2[1][-1], 4) + self.assertEqual(ar2[-1][-1], 0) + self.assertEqual(ar2[-2][-2], 1) + self.assertEqual(ar2[-3][-1], 2) + + # Test advanced slicing + ar[:] = 0 + ar2 = ar[:, 1] + ar2[:] = [99] * len(ar2) + self.assertEqual(ar2[0], 99) + self.assertEqual(ar2[-1], 99) + self.assertEqual(ar2[-2], 99) + self.assertEqual(ar2[2], 99) + self.assertEqual(ar[0, 1], 99) + self.assertEqual(ar[1, 1], 99) + self.assertEqual(ar[2, 1], 99) + self.assertEqual(ar[-1, 1], 99) + self.assertEqual(ar[-2, 1], 99) + + # Cases where a 2d array should have a dimension of length 1. + ar2 = ar[1:2, :] + self.assertEqual(ar2.shape, (1, ar.shape[1])) + ar2 = ar[:, 1:2] + self.assertEqual(ar2.shape, (ar.shape[0], 1)) + sf2 = pygame.Surface((1, 5), 0, 32) + ar2 = pygame.PixelArray(sf2) + self.assertEqual(ar2.shape, sf2.get_size()) + sf2 = pygame.Surface((7, 1), 0, 32) + ar2 = pygame.PixelArray(sf2) + self.assertEqual(ar2.shape, sf2.get_size()) + + # Array has a single ellipsis subscript: the identity operator + ar2 = ar[...] + self.assertTrue(ar2 is ar) + + # Ensure x and y are freed for p = array[x, y] + # Bug fix: reference counting + if hasattr(sys, "getrefcount"): + + class Int(int): + """Unique int instances""" + + pass + + sf = pygame.Surface((2, 2), 0, 32) + ar = pygame.PixelArray(sf) + x, y = Int(0), Int(1) + rx_before, ry_before = sys.getrefcount(x), sys.getrefcount(y) + p = ar[x, y] + rx_after, ry_after = sys.getrefcount(x), sys.getrefcount(y) + self.assertEqual(rx_after, rx_before) + self.assertEqual(ry_after, ry_before) + + def test_ass_subscript(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((6, 8), 0, bpp) + sf.fill((255, 255, 255)) + ar = pygame.PixelArray(sf) + + # Test ellipse working + ar[..., ...] = (0, 0, 0) + self.assertEqual(ar[0, 0], 0) + self.assertEqual(ar[1, 0], 0) + self.assertEqual(ar[-1, -1], 0) + ar[...,] = (0, 0, 255) + self.assertEqual(ar[0, 0], sf.map_rgb((0, 0, 255))) + self.assertEqual(ar[1, 0], sf.map_rgb((0, 0, 255))) + self.assertEqual(ar[-1, -1], sf.map_rgb((0, 0, 255))) + ar[:, ...] = (255, 0, 0) + self.assertEqual(ar[0, 0], sf.map_rgb((255, 0, 0))) + self.assertEqual(ar[1, 0], sf.map_rgb((255, 0, 0))) + self.assertEqual(ar[-1, -1], sf.map_rgb((255, 0, 0))) + ar[...] = (0, 255, 0) + self.assertEqual(ar[0, 0], sf.map_rgb((0, 255, 0))) + self.assertEqual(ar[1, 0], sf.map_rgb((0, 255, 0))) + self.assertEqual(ar[-1, -1], sf.map_rgb((0, 255, 0))) + + # Ensure x and y are freed for array[x, y] = p + # Bug fix: reference counting + if hasattr(sys, "getrefcount"): + + class Int(int): + """Unique int instances""" + + pass + + sf = pygame.Surface((2, 2), 0, 32) + ar = pygame.PixelArray(sf) + x, y = Int(0), Int(1) + rx_before, ry_before = sys.getrefcount(x), sys.getrefcount(y) + ar[x, y] = 0 + rx_after, ry_after = sys.getrefcount(x), sys.getrefcount(y) + self.assertEqual(rx_after, rx_before) + self.assertEqual(ry_after, ry_before) + + def test_pixels_field(self): + for bpp in [1, 2, 3, 4]: + sf = pygame.Surface((11, 7), 0, bpp * 8) + ar = pygame.PixelArray(sf) + ar2 = ar[1:, :] + self.assertEqual(ar2._pixels_address - ar._pixels_address, ar.itemsize) + ar2 = ar[:, 1:] + self.assertEqual(ar2._pixels_address - ar._pixels_address, ar.strides[1]) + ar2 = ar[::-1, :] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + (ar.shape[0] - 1) * ar.itemsize, + ) + ar2 = ar[::-2, :] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + (ar.shape[0] - 1) * ar.itemsize, + ) + ar2 = ar[:, ::-1] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + (ar.shape[1] - 1) * ar.strides[1], + ) + ar3 = ar2[::-1, :] + self.assertEqual( + ar3._pixels_address - ar._pixels_address, + (ar.shape[0] - 1) * ar.strides[0] + (ar.shape[1] - 1) * ar.strides[1], + ) + ar2 = ar[:, ::-2] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + (ar.shape[1] - 1) * ar.strides[1], + ) + ar2 = ar[2::, 3::] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + ar.strides[0] * 2 + ar.strides[1] * 3, + ) + ar2 = ar[2::2, 3::4] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + ar.strides[0] * 2 + ar.strides[1] * 3, + ) + ar2 = ar[9:2:-1, :] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, ar.strides[0] * 9 + ) + ar2 = ar[:, 5:2:-1] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, ar.strides[1] * 5 + ) + ##? ar2 = ar[:,9:2:-1] + + def test_make_surface(self): + bg_color = pygame.Color(255, 255, 255) + fg_color = pygame.Color(128, 100, 0) + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((10, 20), 0, bpp) + bg_color_adj = sf.unmap_rgb(sf.map_rgb(bg_color)) + fg_color_adj = sf.unmap_rgb(sf.map_rgb(fg_color)) + sf.fill(bg_color_adj) + sf.fill(fg_color_adj, (2, 5, 4, 11)) + ar = pygame.PixelArray(sf) + newsf = ar[::2, ::2].make_surface() + rect = newsf.get_rect() + self.assertEqual(rect.width, 5) + self.assertEqual(rect.height, 10) + for p in [ + (0, 2), + (0, 3), + (1, 2), + (2, 2), + (3, 2), + (3, 3), + (0, 7), + (0, 8), + (1, 8), + (2, 8), + (3, 8), + (3, 7), + ]: + self.assertEqual(newsf.get_at(p), bg_color_adj) + for p in [(1, 3), (2, 3), (1, 5), (2, 5), (1, 7), (2, 7)]: + self.assertEqual(newsf.get_at(p), fg_color_adj) + + # Bug when array width is not a multiple of the slice step. + w = 17 + lst = list(range(w)) + w_slice = len(lst[::2]) + h = 3 + sf = pygame.Surface((w, h), 0, 32) + ar = pygame.PixelArray(sf) + ar2 = ar[::2, :] + sf2 = ar2.make_surface() + w2, h2 = sf2.get_size() + self.assertEqual(w2, w_slice) + self.assertEqual(h2, h) + + # Bug when array height is not a multiple of the slice step. + # This can hang the Python interpreter. + h = 17 + lst = list(range(h)) + h_slice = len(lst[::2]) + w = 3 + sf = pygame.Surface((w, h), 0, 32) + ar = pygame.PixelArray(sf) + ar2 = ar[:, ::2] + sf2 = ar2.make_surface() # Hangs here. + w2, h2 = sf2.get_size() + self.assertEqual(w2, w) + self.assertEqual(h2, h_slice) + + def test_make_surface__subclassed_surface(self): + """Ensure make_surface can handle subclassed surfaces.""" + expected_size = (3, 5) + expected_flags = 0 + expected_depth = 32 + original_surface = SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) + pixelarray = pygame.PixelArray(original_surface) + + surface = pixelarray.make_surface() + + self.assertIsNot(surface, original_surface) + self.assertIsInstance(surface, pygame.Surface) + self.assertNotIsInstance(surface, SurfaceSubclass) + self.assertEqual(surface.get_size(), expected_size) + self.assertEqual(surface.get_flags(), expected_flags) + self.assertEqual(surface.get_bitsize(), expected_depth) + + def test_iter(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((5, 10), 0, bpp) + ar = pygame.PixelArray(sf) + iterations = 0 + for col in ar: + self.assertEqual(len(col), 10) + iterations += 1 + self.assertEqual(iterations, 5) + + def test_replace(self): + # print "replace start" + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((10, 10), 0, bpp) + sf.fill((255, 0, 0)) + rval = sf.map_rgb((0, 0, 255)) + oval = sf.map_rgb((255, 0, 0)) + ar = pygame.PixelArray(sf) + ar[::2].replace((255, 0, 0), (0, 0, 255)) + self.assertEqual(ar[0][0], rval) + self.assertEqual(ar[1][0], oval) + self.assertEqual(ar[2][3], rval) + self.assertEqual(ar[3][6], oval) + self.assertEqual(ar[8][9], rval) + self.assertEqual(ar[9][9], oval) + + ar[::2].replace((0, 0, 255), (255, 0, 0), weights=(10, 20, 50)) + self.assertEqual(ar[0][0], oval) + self.assertEqual(ar[2][3], oval) + self.assertEqual(ar[3][6], oval) + self.assertEqual(ar[8][9], oval) + self.assertEqual(ar[9][9], oval) + # print "replace end" + + def test_extract(self): + # print "extract start" + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((10, 10), 0, bpp) + sf.fill((0, 0, 255)) + sf.fill((255, 0, 0), (2, 2, 6, 6)) + + white = sf.map_rgb((255, 255, 255)) + black = sf.map_rgb((0, 0, 0)) + + ar = pygame.PixelArray(sf) + newar = ar.extract((255, 0, 0)) + + self.assertEqual(newar[0][0], black) + self.assertEqual(newar[1][0], black) + self.assertEqual(newar[2][3], white) + self.assertEqual(newar[3][6], white) + self.assertEqual(newar[8][9], black) + self.assertEqual(newar[9][9], black) + + newar = ar.extract((255, 0, 0), weights=(10, 0.1, 50)) + self.assertEqual(newar[0][0], black) + self.assertEqual(newar[1][0], black) + self.assertEqual(newar[2][3], white) + self.assertEqual(newar[3][6], white) + self.assertEqual(newar[8][9], black) + self.assertEqual(newar[9][9], black) + # print "extract end" + + def test_2dslice_assignment(self): + w = 2 * 5 * 8 + h = 3 * 5 * 9 + sf = pygame.Surface((w, h), 0, 32) + ar = pygame.PixelArray(sf) + size = (w, h) + strides = (1, w) + offset = 0 + self._test_assignment(sf, ar, size, strides, offset) + xslice = slice(None, None, 2) + yslice = slice(None, None, 3) + ar, size, strides, offset = self._array_slice( + ar, size, (xslice, yslice), strides, offset + ) + self._test_assignment(sf, ar, size, strides, offset) + xslice = slice(5, None, 5) + yslice = slice(5, None, 5) + ar, size, strides, offset = self._array_slice( + ar, size, (xslice, yslice), strides, offset + ) + self._test_assignment(sf, ar, size, strides, offset) + + def _test_assignment(self, sf, ar, ar_size, ar_strides, ar_offset): + self.assertEqual(ar.shape, ar_size) + ar_w, ar_h = ar_size + ar_xstride, ar_ystride = ar_strides + sf_w, sf_h = sf.get_size() + black = pygame.Color("black") + color = pygame.Color(0, 0, 12) + pxcolor = sf.map_rgb(color) + sf.fill(black) + for ar_x, ar_y in [ + (0, 0), + (0, ar_h - 4), + (ar_w - 3, 0), + (0, ar_h - 1), + (ar_w - 1, 0), + (ar_w - 1, ar_h - 1), + ]: + sf_offset = ar_offset + ar_x * ar_xstride + ar_y * ar_ystride + sf_y = sf_offset // sf_w + sf_x = sf_offset - sf_y * sf_w + sf_posn = (sf_x, sf_y) + sf_pix = sf.get_at(sf_posn) + self.assertEqual( + sf_pix, + black, + "at pixarr posn (%i, %i) (surf posn (%i, %i)): " + "%s != %s" % (ar_x, ar_y, sf_x, sf_y, sf_pix, black), + ) + ar[ar_x, ar_y] = pxcolor + sf_pix = sf.get_at(sf_posn) + self.assertEqual( + sf_pix, + color, + "at pixarr posn (%i, %i) (surf posn (%i, %i)): " + "%s != %s" % (ar_x, ar_y, sf_x, sf_y, sf_pix, color), + ) + + def _array_slice(self, ar, size, slices, strides, offset): + ar = ar[slices] + xslice, yslice = slices + w, h = size + xstart, xstop, xstep = xslice.indices(w) + ystart, ystop, ystep = yslice.indices(h) + w = (xstop - xstart + xstep - 1) // xstep + h = (ystop - ystart + ystep - 1) // ystep + xstride, ystride = strides + offset += xstart * xstride + ystart * ystride + xstride *= xstep + ystride *= ystep + return ar, (w, h), (xstride, ystride), offset + + def test_array_properties(self): + # itemsize, ndim, shape, and strides. + for bpp in [1, 2, 3, 4]: + sf = pygame.Surface((2, 2), 0, bpp * 8) + ar = pygame.PixelArray(sf) + self.assertEqual(ar.itemsize, bpp) + + for shape in [(4, 16), (5, 13)]: + w, h = shape + sf = pygame.Surface(shape, 0, 32) + bpp = sf.get_bytesize() + pitch = sf.get_pitch() + ar = pygame.PixelArray(sf) + self.assertEqual(ar.ndim, 2) + self.assertEqual(ar.shape, shape) + self.assertEqual(ar.strides, (bpp, pitch)) + ar2 = ar[::2, :] + w2 = len(([0] * w)[::2]) + self.assertEqual(ar2.ndim, 2) + self.assertEqual(ar2.shape, (w2, h)) + self.assertEqual(ar2.strides, (2 * bpp, pitch)) + ar2 = ar[:, ::2] + h2 = len(([0] * h)[::2]) + self.assertEqual(ar2.ndim, 2) + self.assertEqual(ar2.shape, (w, h2)) + self.assertEqual(ar2.strides, (bpp, 2 * pitch)) + ar2 = ar[1] + self.assertEqual(ar2.ndim, 1) + self.assertEqual(ar2.shape, (h,)) + self.assertEqual(ar2.strides, (pitch,)) + ar2 = ar[:, 1] + self.assertEqual(ar2.ndim, 1) + self.assertEqual(ar2.shape, (w,)) + self.assertEqual(ar2.strides, (bpp,)) + + def test_self_assign(self): + # This differs from NumPy arrays. + w = 10 + max_x = w - 1 + h = 20 + max_y = h - 1 + for bpp in [1, 2, 3, 4]: + sf = pygame.Surface((w, h), 0, bpp * 8) + ar = pygame.PixelArray(sf) + for i in range(w * h): + ar[i % w, i // w] = i + ar[:, :] = ar[::-1, :] + for i in range(w * h): + self.assertEqual(ar[max_x - i % w, i // w], i) + ar = pygame.PixelArray(sf) + for i in range(w * h): + ar[i % w, i // w] = i + ar[:, :] = ar[:, ::-1] + for i in range(w * h): + self.assertEqual(ar[i % w, max_y - i // w], i) + ar = pygame.PixelArray(sf) + for i in range(w * h): + ar[i % w, i // w] = i + ar[:, :] = ar[::-1, ::-1] + for i in range(w * h): + self.assertEqual(ar[max_x - i % w, max_y - i // w], i) + + def test_color_value(self): + # Confirm that a PixelArray slice assignment distinguishes between + # pygame.Color and tuple objects as single (r, g, b[, a]) colors + # and other sequences as sequences of colors to be treated as + # slices. + sf = pygame.Surface((5, 5), 0, 32) + ar = pygame.PixelArray(sf) + index = slice(None, None, 1) + ar.__setitem__(index, (1, 2, 3)) + self.assertEqual(ar[0, 0], sf.map_rgb((1, 2, 3))) + ar.__setitem__(index, pygame.Color(10, 11, 12)) + self.assertEqual(ar[0, 0], sf.map_rgb((10, 11, 12))) + self.assertRaises(ValueError, ar.__setitem__, index, (1, 2, 3, 4, 5)) + self.assertRaises(ValueError, ar.__setitem__, (index, index), (1, 2, 3, 4, 5)) + self.assertRaises(ValueError, ar.__setitem__, index, [1, 2, 3]) + self.assertRaises(ValueError, ar.__setitem__, (index, index), [1, 2, 3]) + sf = pygame.Surface((3, 3), 0, 32) + ar = pygame.PixelArray(sf) + ar[:] = (20, 30, 40) + self.assertEqual(ar[0, 0], sf.map_rgb((20, 30, 40))) + ar[:] = [20, 30, 40] + self.assertEqual(ar[0, 0], 20) + self.assertEqual(ar[1, 0], 30) + self.assertEqual(ar[2, 0], 40) + + def test_transpose(self): + # PixelArray.transpose(): swap axis on a 2D array, add a length + # 1 x axis to a 1D array. + sf = pygame.Surface((3, 7), 0, 32) + ar = pygame.PixelArray(sf) + w, h = ar.shape + dx, dy = ar.strides + for i in range(w * h): + x = i % w + y = i // w + ar[x, y] = i + ar_t = ar.transpose() + self.assertEqual(ar_t.shape, (h, w)) + self.assertEqual(ar_t.strides, (dy, dx)) + for i in range(w * h): + x = i % w + y = i // w + self.assertEqual(ar_t[y, x], ar[x, y]) + ar1D = ar[0] + ar2D = ar1D.transpose() + self.assertEqual(ar2D.shape, (1, h)) + for y in range(h): + self.assertEqual(ar1D[y], ar2D[0, y]) + ar1D = ar[:, 0] + ar2D = ar1D.transpose() + self.assertEqual(ar2D.shape, (1, w)) + for x in range(2): + self.assertEqual(ar1D[x], ar2D[0, x]) + + def test_length_1_dimension_broadcast(self): + w = 5 + sf = pygame.Surface((w, w), 0, 32) + ar = pygame.PixelArray(sf) + # y-axis broadcast. + sf_x = pygame.Surface((w, 1), 0, 32) + ar_x = pygame.PixelArray(sf_x) + for i in range(w): + ar_x[i, 0] = (w + 1) * 10 + ar[...] = ar_x + for y in range(w): + for x in range(w): + self.assertEqual(ar[x, y], ar_x[x, 0]) + # x-axis broadcast. + ar[...] = 0 + sf_y = pygame.Surface((1, w), 0, 32) + ar_y = pygame.PixelArray(sf_y) + for i in range(w): + ar_y[0, i] = (w + 1) * 10 + ar[...] = ar_y + for x in range(w): + for y in range(w): + self.assertEqual(ar[x, y], ar_y[0, y]) + # (1, 1) array broadcast. + ar[...] = 0 + sf_1px = pygame.Surface((1, 1), 0, 32) + ar_1px = pygame.PixelArray(sf_1px) + ar_1px[0, 0] = 42 # Well it had to show up somewhere. + ar[...] = ar_1px + for y in range(w): + for x in range(w): + self.assertEqual(ar[x, y], 42) + + def test_assign_size_mismatch(self): + sf = pygame.Surface((7, 11), 0, 32) + ar = pygame.PixelArray(sf) + self.assertRaises(ValueError, ar.__setitem__, Ellipsis, ar[:, 0:2]) + self.assertRaises(ValueError, ar.__setitem__, Ellipsis, ar[0:2, :]) + + def test_repr(self): + # Python 3.x bug: the tp_repr slot function returned NULL instead + # of a Unicode string, triggering an exception. + sf = pygame.Surface((3, 1), pygame.SRCALPHA, 16) + ar = pygame.PixelArray(sf) + ar[...] = 42 + pixel = sf.get_at_mapped((0, 0)) + self.assertEqual(repr(ar), type(ar).__name__ + "([\n [42, 42, 42]]\n)") + + +class PixelArrayArrayInterfaceTest(unittest.TestCase, TestMixin): + @unittest.skipIf(IS_PYPY, "skipping for PyPy (why?)") + def test_basic(self): + # Check unchanging fields. + sf = pygame.Surface((2, 2), 0, 32) + ar = pygame.PixelArray(sf) + + ai = arrinter.ArrayInterface(ar) + self.assertEqual(ai.two, 2) + self.assertEqual(ai.typekind, "u") + self.assertEqual(ai.nd, 2) + self.assertEqual(ai.data, ar._pixels_address) + + @unittest.skipIf(IS_PYPY, "skipping for PyPy (why?)") + def test_shape(self): + + for shape in [[4, 16], [5, 13]]: + w, h = shape + sf = pygame.Surface(shape, 0, 32) + ar = pygame.PixelArray(sf) + ai = arrinter.ArrayInterface(ar) + ai_shape = [ai.shape[i] for i in range(ai.nd)] + self.assertEqual(ai_shape, shape) + ar2 = ar[::2, :] + ai2 = arrinter.ArrayInterface(ar2) + w2 = len(([0] * w)[::2]) + ai_shape = [ai2.shape[i] for i in range(ai2.nd)] + self.assertEqual(ai_shape, [w2, h]) + ar2 = ar[:, ::2] + ai2 = arrinter.ArrayInterface(ar2) + h2 = len(([0] * h)[::2]) + ai_shape = [ai2.shape[i] for i in range(ai2.nd)] + self.assertEqual(ai_shape, [w, h2]) + + @unittest.skipIf(IS_PYPY, "skipping for PyPy (why?)") + def test_itemsize(self): + for bytes_per_pixel in range(1, 5): + bits_per_pixel = 8 * bytes_per_pixel + sf = pygame.Surface((2, 2), 0, bits_per_pixel) + ar = pygame.PixelArray(sf) + ai = arrinter.ArrayInterface(ar) + self.assertEqual(ai.itemsize, bytes_per_pixel) + + @unittest.skipIf(IS_PYPY, "skipping for PyPy (why?)") + def test_flags(self): + aim = arrinter + common_flags = aim.PAI_NOTSWAPPED | aim.PAI_WRITEABLE | aim.PAI_ALIGNED + s = pygame.Surface((10, 2), 0, 32) + ar = pygame.PixelArray(s) + ai = aim.ArrayInterface(ar) + self.assertEqual(ai.flags, common_flags | aim.PAI_FORTRAN) + + ar2 = ar[::2, :] + ai = aim.ArrayInterface(ar2) + self.assertEqual(ai.flags, common_flags) + + s = pygame.Surface((8, 2), 0, 24) + ar = pygame.PixelArray(s) + ai = aim.ArrayInterface(ar) + self.assertEqual(ai.flags, common_flags | aim.PAI_FORTRAN) + + s = pygame.Surface((7, 2), 0, 24) + ar = pygame.PixelArray(s) + ai = aim.ArrayInterface(ar) + self.assertEqual(ai.flags, common_flags) + + def test_slicing(self): + # This will implicitly test data and strides fields. + # + # Need an 8 bit test surfaces because pixelcopy.make_surface + # returns an 8 bit surface for a 2d array. + + factors = [7, 3, 11] + + w = reduce(operator.mul, factors, 1) + h = 13 + sf = pygame.Surface((w, h), 0, 8) + color = sf.map_rgb((1, 17, 128)) + ar = pygame.PixelArray(sf) + for f in factors[:-1]: + w = w // f + sf.fill((0, 0, 0)) + ar = ar[f : f + w, :] + ar[0][0] = color + ar[-1][-2] = color + ar[0][-3] = color + sf2 = ar.make_surface() + sf3 = pygame.pixelcopy.make_surface(ar) + self.assert_surfaces_equal(sf3, sf2) + + h = reduce(operator.mul, factors, 1) + w = 13 + sf = pygame.Surface((w, h), 0, 8) + color = sf.map_rgb((1, 17, 128)) + ar = pygame.PixelArray(sf) + for f in factors[:-1]: + h = h // f + sf.fill((0, 0, 0)) + ar = ar[:, f : f + h] + ar[0][0] = color + ar[-1][-2] = color + ar[0][-3] = color + sf2 = ar.make_surface() + sf3 = pygame.pixelcopy.make_surface(ar) + self.assert_surfaces_equal(sf3, sf2) + + w = 20 + h = 10 + sf = pygame.Surface((w, h), 0, 8) + color = sf.map_rgb((1, 17, 128)) + ar = pygame.PixelArray(sf) + for slices in [ + (slice(w), slice(h)), + (slice(0, w, 2), slice(h)), + (slice(0, w, 3), slice(h)), + (slice(w), slice(0, h, 2)), + (slice(w), slice(0, h, 3)), + (slice(0, w, 2), slice(0, h, 2)), + (slice(0, w, 3), slice(0, h, 3)), + ]: + sf.fill((0, 0, 0)) + ar2 = ar[slices] + ar2[0][0] = color + ar2[-1][-2] = color + ar2[0][-3] = color + sf2 = ar2.make_surface() + sf3 = pygame.pixelcopy.make_surface(ar2) + self.assert_surfaces_equal(sf3, sf2) + + +@unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") +class PixelArrayNewBufferTest(unittest.TestCase, TestMixin): + + if pygame.HAVE_NEWBUF: + from pygame.tests.test_utils import buftools + + bitsize_to_format = {8: "B", 16: "=H", 24: "3x", 32: "=I"} + + def test_newbuf_2D(self): + buftools = self.buftools + Importer = buftools.Importer + + for bit_size in [8, 16, 24, 32]: + s = pygame.Surface((10, 2), 0, bit_size) + ar = pygame.PixelArray(s) + format = self.bitsize_to_format[bit_size] + itemsize = ar.itemsize + shape = ar.shape + w, h = shape + strides = ar.strides + length = w * h * itemsize + imp = Importer(ar, buftools.PyBUF_FULL) + self.assertTrue(imp.obj, ar) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 2) + self.assertEqual(imp.itemsize, itemsize) + self.assertEqual(imp.format, format) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertTrue(imp.suboffsets is None) + self.assertEqual(imp.buf, s._pixels_address) + + s = pygame.Surface((8, 16), 0, 32) + ar = pygame.PixelArray(s) + format = self.bitsize_to_format[s.get_bitsize()] + itemsize = ar.itemsize + shape = ar.shape + w, h = shape + strides = ar.strides + length = w * h * itemsize + imp = Importer(ar, buftools.PyBUF_SIMPLE) + self.assertTrue(imp.obj, ar) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 0) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertTrue(imp.shape is None) + self.assertTrue(imp.strides is None) + self.assertTrue(imp.suboffsets is None) + self.assertEqual(imp.buf, s._pixels_address) + imp = Importer(ar, buftools.PyBUF_FORMAT) + self.assertEqual(imp.ndim, 0) + self.assertEqual(imp.format, format) + imp = Importer(ar, buftools.PyBUF_WRITABLE) + self.assertEqual(imp.ndim, 0) + self.assertTrue(imp.format is None) + imp = Importer(ar, buftools.PyBUF_F_CONTIGUOUS) + self.assertEqual(imp.ndim, 2) + self.assertTrue(imp.format is None) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + imp = Importer(ar, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertEqual(imp.ndim, 2) + self.assertTrue(imp.format is None) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_ND) + + ar_sliced = ar[:, ::2] + format = self.bitsize_to_format[s.get_bitsize()] + itemsize = ar_sliced.itemsize + shape = ar_sliced.shape + w, h = shape + strides = ar_sliced.strides + length = w * h * itemsize + imp = Importer(ar_sliced, buftools.PyBUF_STRIDED) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 2) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertEqual(imp.buf, s._pixels_address) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises( + BufferError, Importer, ar_sliced, buftools.PyBUF_ANY_CONTIGUOUS + ) + + ar_sliced = ar[::2, :] + format = self.bitsize_to_format[s.get_bitsize()] + itemsize = ar_sliced.itemsize + shape = ar_sliced.shape + w, h = shape + strides = ar_sliced.strides + length = w * h * itemsize + imp = Importer(ar_sliced, buftools.PyBUF_STRIDED) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 2) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertEqual(imp.buf, s._pixels_address) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises( + BufferError, Importer, ar_sliced, buftools.PyBUF_ANY_CONTIGUOUS + ) + + s2 = s.subsurface((2, 3, 5, 7)) + ar = pygame.PixelArray(s2) + format = self.bitsize_to_format[s.get_bitsize()] + itemsize = ar.itemsize + shape = ar.shape + w, h = shape + strides = ar.strides + length = w * h * itemsize + imp = Importer(ar, buftools.PyBUF_STRIDES) + self.assertTrue(imp.obj, ar) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 2) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertTrue(imp.suboffsets is None) + self.assertEqual(imp.buf, s2._pixels_address) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_FORMAT) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_ANY_CONTIGUOUS) + + def test_newbuf_1D(self): + buftools = self.buftools + Importer = buftools.Importer + + s = pygame.Surface((2, 16), 0, 32) + ar_2D = pygame.PixelArray(s) + x = 0 + ar = ar_2D[x] + format = self.bitsize_to_format[s.get_bitsize()] + itemsize = ar.itemsize + shape = ar.shape + h = shape[0] + strides = ar.strides + length = h * itemsize + buf = s._pixels_address + x * itemsize + imp = Importer(ar, buftools.PyBUF_STRIDES) + self.assertTrue(imp.obj, ar) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 1) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertTrue(imp.suboffsets is None) + self.assertEqual(imp.buf, buf) + imp = Importer(ar, buftools.PyBUF_FULL) + self.assertEqual(imp.ndim, 1) + self.assertEqual(imp.format, format) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_FORMAT) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_ANY_CONTIGUOUS) + y = 10 + ar = ar_2D[:, y] + shape = ar.shape + w = shape[0] + strides = ar.strides + length = w * itemsize + buf = s._pixels_address + y * s.get_pitch() + imp = Importer(ar, buftools.PyBUF_FULL) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 1) + self.assertEqual(imp.itemsize, itemsize) + self.assertEqual(imp.format, format) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertEqual(imp.buf, buf) + self.assertTrue(imp.suboffsets is None) + imp = Importer(ar, buftools.PyBUF_SIMPLE) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 0) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertTrue(imp.shape is None) + self.assertTrue(imp.strides is None) + imp = Importer(ar, buftools.PyBUF_ND) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 1) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertTrue(imp.strides is None) + imp = Importer(ar, buftools.PyBUF_C_CONTIGUOUS) + self.assertEqual(imp.ndim, 1) + imp = Importer(ar, buftools.PyBUF_F_CONTIGUOUS) + self.assertEqual(imp.ndim, 1) + imp = Importer(ar, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertEqual(imp.ndim, 1) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/pixelcopy_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/pixelcopy_test.py new file mode 100644 index 0000000..3dab026 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/pixelcopy_test.py @@ -0,0 +1,710 @@ +import ctypes +import platform +import unittest + +try: + from pygame.tests.test_utils import arrinter +except NameError: + pass +import pygame +from pygame.locals import * +from pygame.pixelcopy import surface_to_array, map_array, array_to_surface, make_surface + +IS_PYPY = "PyPy" == platform.python_implementation() + + +def unsigned32(i): + """cast signed 32 bit integer to an unsigned integer""" + return i & 0xFFFFFFFF + + +class PixelcopyModuleTest(unittest.TestCase): + + bitsizes = [8, 16, 32] + + test_palette = [ + (0, 0, 0, 255), + (10, 30, 60, 255), + (25, 75, 100, 255), + (100, 150, 200, 255), + (0, 100, 200, 255), + ] + + surf_size = (10, 12) + test_points = [ + ((0, 0), 1), + ((4, 5), 1), + ((9, 0), 2), + ((5, 5), 2), + ((0, 11), 3), + ((4, 6), 3), + ((9, 11), 4), + ((5, 6), 4), + ] + + def __init__(self, *args, **kwds): + pygame.display.init() + try: + unittest.TestCase.__init__(self, *args, **kwds) + self.sources = [ + self._make_src_surface(8), + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] + finally: + pygame.display.quit() + + def _make_surface(self, bitsize, srcalpha=False, palette=None): + if palette is None: + palette = self.test_palette + flags = 0 + if srcalpha: + flags |= SRCALPHA + surf = pygame.Surface(self.surf_size, flags, bitsize) + if bitsize == 8: + surf.set_palette([c[:3] for c in palette]) + return surf + + def _fill_surface(self, surf, palette=None): + if palette is None: + palette = self.test_palette + surf.fill(palette[1], (0, 0, 5, 6)) + surf.fill(palette[2], (5, 0, 5, 6)) + surf.fill(palette[3], (0, 6, 5, 6)) + surf.fill(palette[4], (5, 6, 5, 6)) + + def _make_src_surface(self, bitsize, srcalpha=False, palette=None): + surf = self._make_surface(bitsize, srcalpha, palette) + self._fill_surface(surf, palette) + return surf + + def setUp(self): + pygame.display.init() + + def tearDown(self): + pygame.display.quit() + + def test_surface_to_array_2d(self): + alpha_color = (0, 0, 0, 128) + + for surf in self.sources: + src_bitsize = surf.get_bitsize() + for dst_bitsize in self.bitsizes: + # dst in a surface standing in for a 2 dimensional array + # of unsigned integers. The byte order is system dependent. + dst = pygame.Surface(surf.get_size(), 0, dst_bitsize) + dst.fill((0, 0, 0, 0)) + view = dst.get_view("2") + self.assertFalse(surf.get_locked()) + if dst_bitsize < src_bitsize: + self.assertRaises(ValueError, surface_to_array, view, surf) + self.assertFalse(surf.get_locked()) + continue + surface_to_array(view, surf) + self.assertFalse(surf.get_locked()) + for posn, i in self.test_points: + sp = surf.get_at_mapped(posn) + dp = dst.get_at_mapped(posn) + self.assertEqual( + dp, + sp, + "%s != %s: flags: %i" + ", bpp: %i, posn: %s" + % (dp, sp, surf.get_flags(), surf.get_bitsize(), posn), + ) + del view + + if surf.get_masks()[3]: + dst.fill((0, 0, 0, 0)) + view = dst.get_view("2") + posn = (2, 1) + surf.set_at(posn, alpha_color) + self.assertFalse(surf.get_locked()) + surface_to_array(view, surf) + self.assertFalse(surf.get_locked()) + sp = surf.get_at_mapped(posn) + dp = dst.get_at_mapped(posn) + self.assertEqual( + dp, sp, "%s != %s: bpp: %i" % (dp, sp, surf.get_bitsize()) + ) + + if IS_PYPY: + return + # Swapped endian destination array + pai_flags = arrinter.PAI_ALIGNED | arrinter.PAI_WRITEABLE + for surf in self.sources: + for itemsize in [1, 2, 4, 8]: + if itemsize < surf.get_bytesize(): + continue + a = arrinter.Array(surf.get_size(), "u", itemsize, flags=pai_flags) + surface_to_array(a, surf) + for posn, i in self.test_points: + sp = unsigned32(surf.get_at_mapped(posn)) + dp = a[posn] + self.assertEqual( + dp, + sp, + "%s != %s: itemsize: %i, flags: %i" + ", bpp: %i, posn: %s" + % ( + dp, + sp, + itemsize, + surf.get_flags(), + surf.get_bitsize(), + posn, + ), + ) + + def test_surface_to_array_3d(self): + self.iter_surface_to_array_3d((0xFF, 0xFF00, 0xFF0000, 0)) + self.iter_surface_to_array_3d((0xFF0000, 0xFF00, 0xFF, 0)) + + def iter_surface_to_array_3d(self, rgba_masks): + dst = pygame.Surface(self.surf_size, 0, 24, masks=rgba_masks) + + for surf in self.sources: + dst.fill((0, 0, 0, 0)) + src_bitsize = surf.get_bitsize() + view = dst.get_view("3") + self.assertFalse(surf.get_locked()) + surface_to_array(view, surf) + self.assertFalse(surf.get_locked()) + for posn, i in self.test_points: + sc = surf.get_at(posn)[0:3] + dc = dst.get_at(posn)[0:3] + self.assertEqual( + dc, + sc, + "%s != %s: flags: %i" + ", bpp: %i, posn: %s" + % (dc, sc, surf.get_flags(), surf.get_bitsize(), posn), + ) + view = None + + def test_map_array(self): + targets = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + source = pygame.Surface( + self.surf_size, 0, 24, masks=[0xFF, 0xFF00, 0xFF0000, 0] + ) + self._fill_surface(source) + source_view = source.get_view("3") # (w, h, 3) + for t in targets: + map_array(t.get_view("2"), source_view, t) + for posn, i in self.test_points: + sc = t.map_rgb(source.get_at(posn)) + dc = t.get_at_mapped(posn) + self.assertEqual( + dc, + sc, + "%s != %s: flags: %i" + ", bpp: %i, posn: %s" + % (dc, sc, t.get_flags(), t.get_bitsize(), posn), + ) + + color = pygame.Color("salmon") + color.set_length(3) + for t in targets: + map_array(t.get_view("2"), color, t) + sc = t.map_rgb(color) + for posn, i in self.test_points: + dc = t.get_at_mapped(posn) + self.assertEqual( + dc, + sc, + "%s != %s: flags: %i" + ", bpp: %i, posn: %s" + % (dc, sc, t.get_flags(), t.get_bitsize(), posn), + ) + + # mismatched shapes + w, h = source.get_size() + target = pygame.Surface((w, h + 1), 0, 32) + self.assertRaises(ValueError, map_array, target, source, target) + target = pygame.Surface((w - 1, h), 0, 32) + self.assertRaises(ValueError, map_array, target, source, target) + + def test_array_to_surface_broadcasting(self): + # target surfaces + targets = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + + w, h = self.surf_size + + # broadcast column + column = pygame.Surface((1, h), 0, 32) + for target in targets: + source = pygame.Surface((1, h), 0, target) + for y in range(h): + source.set_at((0, y), pygame.Color(y + 1, y + h + 1, y + 2 * h + 1)) + pygame.pixelcopy.surface_to_array(column.get_view("2"), source) + pygame.pixelcopy.array_to_surface(target, column.get_view("2")) + for x in range(w): + for y in range(h): + self.assertEqual( + target.get_at_mapped((x, y)), column.get_at_mapped((0, y)) + ) + + # broadcast row + row = pygame.Surface((w, 1), 0, 32) + for target in targets: + source = pygame.Surface((w, 1), 0, target) + for x in range(w): + source.set_at((x, 0), pygame.Color(x + 1, x + w + 1, x + 2 * w + 1)) + pygame.pixelcopy.surface_to_array(row.get_view("2"), source) + pygame.pixelcopy.array_to_surface(target, row.get_view("2")) + for x in range(w): + for y in range(h): + self.assertEqual( + target.get_at_mapped((x, y)), row.get_at_mapped((x, 0)) + ) + + # broadcast pixel + pixel = pygame.Surface((1, 1), 0, 32) + for target in targets: + source = pygame.Surface((1, 1), 0, target) + source.set_at((0, 0), pygame.Color(13, 47, 101)) + pygame.pixelcopy.surface_to_array(pixel.get_view("2"), source) + pygame.pixelcopy.array_to_surface(target, pixel.get_view("2")) + p = pixel.get_at_mapped((0, 0)) + for x in range(w): + for y in range(h): + self.assertEqual(target.get_at_mapped((x, y)), p) + + +class PixelCopyTestWithArray(unittest.TestCase): + try: + import numpy + except ImportError: + __tags__ = ["ignore", "subprocess_ignore"] + else: + pygame.surfarray.use_arraytype("numpy") + + bitsizes = [8, 16, 32] + + test_palette = [ + (0, 0, 0, 255), + (10, 30, 60, 255), + (25, 75, 100, 255), + (100, 150, 200, 255), + (0, 100, 200, 255), + ] + + surf_size = (10, 12) + test_points = [ + ((0, 0), 1), + ((4, 5), 1), + ((9, 0), 2), + ((5, 5), 2), + ((0, 11), 3), + ((4, 6), 3), + ((9, 11), 4), + ((5, 6), 4), + ] + + pixels2d = set([8, 16, 32]) + pixels3d = set([24, 32]) + array2d = set([8, 16, 24, 32]) + array3d = set([24, 32]) + + def __init__(self, *args, **kwds): + import numpy + + self.dst_types = [numpy.uint8, numpy.uint16, numpy.uint32] + try: + self.dst_types.append(numpy.uint64) + except AttributeError: + pass + pygame.display.init() + try: + unittest.TestCase.__init__(self, *args, **kwds) + self.sources = [ + self._make_src_surface(8), + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] + finally: + pygame.display.quit() + + def _make_surface(self, bitsize, srcalpha=False, palette=None): + if palette is None: + palette = self.test_palette + flags = 0 + if srcalpha: + flags |= SRCALPHA + surf = pygame.Surface(self.surf_size, flags, bitsize) + if bitsize == 8: + surf.set_palette([c[:3] for c in palette]) + return surf + + def _fill_surface(self, surf, palette=None): + if palette is None: + palette = self.test_palette + surf.fill(palette[1], (0, 0, 5, 6)) + surf.fill(palette[2], (5, 0, 5, 6)) + surf.fill(palette[3], (0, 6, 5, 6)) + surf.fill(palette[4], (5, 6, 5, 6)) + + def _make_src_surface(self, bitsize, srcalpha=False, palette=None): + surf = self._make_surface(bitsize, srcalpha, palette) + self._fill_surface(surf, palette) + return surf + + def setUp(self): + pygame.display.init() + + def tearDown(self): + pygame.display.quit() + + def test_surface_to_array_2d(self): + try: + from numpy import empty, dtype + except ImportError: + return + + palette = self.test_palette + alpha_color = (0, 0, 0, 128) + + dst_dims = self.surf_size + destinations = [empty(dst_dims, t) for t in self.dst_types] + if pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN: + swapped_dst = empty(dst_dims, dtype(">u4")) + else: + swapped_dst = empty(dst_dims, dtype("u4")) + else: + swapped_dst = empty(dst_dims, dtype("i", + "!i", + "1i", + "=1i", + "@q", + "q", + "4x", + "8x", + ]: + surface.fill((255, 254, 253)) + exp = Exporter(shape, format=format) + exp._buf[:] = [42] * exp.buflen + array_to_surface(surface, exp) + for x in range(w): + for y in range(h): + self.assertEqual(surface.get_at((x, y)), (42, 42, 42, 255)) + # Some unsupported formats for array_to_surface and a 32 bit surface + for format in ["f", "d", "?", "x", "1x", "2x", "3x", "5x", "6x", "7x", "9x"]: + exp = Exporter(shape, format=format) + self.assertRaises(ValueError, array_to_surface, surface, exp) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/rect_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/rect_test.py new file mode 100644 index 0000000..3a4b63d --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/rect_test.py @@ -0,0 +1,2110 @@ +import math +import sys +import unittest + +from pygame import Rect, Vector2 +from pygame.tests import test_utils + + +PY3 = sys.version_info >= (3, 0, 0) + + +class RectTypeTest(unittest.TestCase): + def _assertCountEqual(self, *args, **kwargs): + # Handle method name differences between Python versions. + if PY3: + self.assertCountEqual(*args, **kwargs) + else: + self.assertItemsEqual(*args, **kwargs) + + def testConstructionXYWidthHeight(self): + r = Rect(1, 2, 3, 4) + self.assertEqual(1, r.left) + self.assertEqual(2, r.top) + self.assertEqual(3, r.width) + self.assertEqual(4, r.height) + + def testConstructionTopLeftSize(self): + r = Rect((1, 2), (3, 4)) + self.assertEqual(1, r.left) + self.assertEqual(2, r.top) + self.assertEqual(3, r.width) + self.assertEqual(4, r.height) + + def testCalculatedAttributes(self): + r = Rect(1, 2, 3, 4) + + self.assertEqual(r.left + r.width, r.right) + self.assertEqual(r.top + r.height, r.bottom) + self.assertEqual((r.width, r.height), r.size) + self.assertEqual((r.left, r.top), r.topleft) + self.assertEqual((r.right, r.top), r.topright) + self.assertEqual((r.left, r.bottom), r.bottomleft) + self.assertEqual((r.right, r.bottom), r.bottomright) + + midx = r.left + r.width // 2 + midy = r.top + r.height // 2 + + self.assertEqual(midx, r.centerx) + self.assertEqual(midy, r.centery) + self.assertEqual((r.centerx, r.centery), r.center) + self.assertEqual((r.centerx, r.top), r.midtop) + self.assertEqual((r.centerx, r.bottom), r.midbottom) + self.assertEqual((r.left, r.centery), r.midleft) + self.assertEqual((r.right, r.centery), r.midright) + + def test_normalize(self): + """Ensures normalize works when width and height are both negative.""" + test_rect = Rect((1, 2), (-3, -6)) + expected_normalized_rect = ( + (test_rect.x + test_rect.w, test_rect.y + test_rect.h), + (-test_rect.w, -test_rect.h), + ) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_normalize__positive_height(self): + """Ensures normalize works with a negative width and a positive height. + """ + test_rect = Rect((1, 2), (-3, 6)) + expected_normalized_rect = ( + (test_rect.x + test_rect.w, test_rect.y), + (-test_rect.w, test_rect.h), + ) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_normalize__positive_width(self): + """Ensures normalize works with a positive width and a negative height. + """ + test_rect = Rect((1, 2), (3, -6)) + expected_normalized_rect = ( + (test_rect.x, test_rect.y + test_rect.h), + (test_rect.w, -test_rect.h), + ) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_normalize__zero_height(self): + """Ensures normalize works with a negative width and a zero height.""" + test_rect = Rect((1, 2), (-3, 0)) + expected_normalized_rect = ( + (test_rect.x + test_rect.w, test_rect.y), + (-test_rect.w, test_rect.h), + ) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_normalize__zero_width(self): + """Ensures normalize works with a zero width and a negative height.""" + test_rect = Rect((1, 2), (0, -6)) + expected_normalized_rect = ( + (test_rect.x, test_rect.y + test_rect.h), + (test_rect.w, -test_rect.h), + ) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_normalize__non_negative(self): + """Ensures normalize works when width and height are both non-negative. + + Tests combinations of positive and zero values for width and height. + The normalize method has no impact when both width and height are + non-negative. + """ + for size in ((3, 6), (3, 0), (0, 6), (0, 0)): + test_rect = Rect((1, 2), size) + expected_normalized_rect = Rect(test_rect) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_x(self): + """Ensures changing the x attribute moves the rect and does not change + the rect's size. + """ + expected_x = 10 + expected_y = 2 + expected_size = (3, 4) + r = Rect((1, expected_y), expected_size) + + r.x = expected_x + + self.assertEqual(r.x, expected_x) + self.assertEqual(r.x, r.left) + self.assertEqual(r.y, expected_y) + self.assertEqual(r.size, expected_size) + + def test_x__invalid_value(self): + """Ensures the x attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.x = value + + def test_x__del(self): + """Ensures the x attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.x + + def test_y(self): + """Ensures changing the y attribute moves the rect and does not change + the rect's size. + """ + expected_x = 1 + expected_y = 20 + expected_size = (3, 4) + r = Rect((expected_x, 2), expected_size) + + r.y = expected_y + + self.assertEqual(r.y, expected_y) + self.assertEqual(r.y, r.top) + self.assertEqual(r.x, expected_x) + self.assertEqual(r.size, expected_size) + + def test_y__invalid_value(self): + """Ensures the y attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.y = value + + def test_y__del(self): + """Ensures the y attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.y + + def test_left(self): + """Changing the left attribute moves the rect and does not change + the rect's width + """ + r = Rect(1, 2, 3, 4) + new_left = 10 + + r.left = new_left + self.assertEqual(new_left, r.left) + self.assertEqual(Rect(new_left, 2, 3, 4), r) + + def test_left__invalid_value(self): + """Ensures the left attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.left = value + + def test_left__del(self): + """Ensures the left attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.left + + def test_right(self): + """Changing the right attribute moves the rect and does not change + the rect's width + """ + r = Rect(1, 2, 3, 4) + new_right = r.right + 20 + expected_left = r.left + 20 + old_width = r.width + + r.right = new_right + self.assertEqual(new_right, r.right) + self.assertEqual(expected_left, r.left) + self.assertEqual(old_width, r.width) + + def test_right__invalid_value(self): + """Ensures the right attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.right = value + + def test_right__del(self): + """Ensures the right attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.right + + def test_top(self): + """Changing the top attribute moves the rect and does not change + the rect's width + """ + r = Rect(1, 2, 3, 4) + new_top = 10 + + r.top = new_top + self.assertEqual(Rect(1, new_top, 3, 4), r) + self.assertEqual(new_top, r.top) + + def test_top__invalid_value(self): + """Ensures the top attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.top = value + + def test_top__del(self): + """Ensures the top attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.top + + def test_bottom(self): + """Changing the bottom attribute moves the rect and does not change + the rect's height + """ + r = Rect(1, 2, 3, 4) + new_bottom = r.bottom + 20 + expected_top = r.top + 20 + old_height = r.height + + r.bottom = new_bottom + self.assertEqual(new_bottom, r.bottom) + self.assertEqual(expected_top, r.top) + self.assertEqual(old_height, r.height) + + def test_bottom__invalid_value(self): + """Ensures the bottom attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.bottom = value + + def test_bottom__del(self): + """Ensures the bottom attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.bottom + + def test_centerx(self): + """Changing the centerx attribute moves the rect and does not change + the rect's width + """ + r = Rect(1, 2, 3, 4) + new_centerx = r.centerx + 20 + expected_left = r.left + 20 + old_width = r.width + + r.centerx = new_centerx + self.assertEqual(new_centerx, r.centerx) + self.assertEqual(expected_left, r.left) + self.assertEqual(old_width, r.width) + + def test_centerx__invalid_value(self): + """Ensures the centerx attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.centerx = value + + def test_centerx__del(self): + """Ensures the centerx attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.centerx + + def test_centery(self): + """Changing the centery attribute moves the rect and does not change + the rect's width + """ + r = Rect(1, 2, 3, 4) + new_centery = r.centery + 20 + expected_top = r.top + 20 + old_height = r.height + + r.centery = new_centery + self.assertEqual(new_centery, r.centery) + self.assertEqual(expected_top, r.top) + self.assertEqual(old_height, r.height) + + def test_centery__invalid_value(self): + """Ensures the centery attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.centery = value + + def test_centery__del(self): + """Ensures the centery attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.centery + + def test_topleft(self): + """Changing the topleft attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.topleft = new_topleft + self.assertEqual(new_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_topleft__invalid_value(self): + """Ensures the topleft attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.topleft = value + + def test_topleft__del(self): + """Ensures the topleft attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.topleft + + def test_bottomleft(self): + """Changing the bottomleft attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_bottomleft = (r.left + 20, r.bottom + 30) + expected_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.bottomleft = new_bottomleft + self.assertEqual(new_bottomleft, r.bottomleft) + self.assertEqual(expected_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_bottomleft__invalid_value(self): + """Ensures the bottomleft attribute handles invalid values correctly. + """ + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.bottomleft = value + + def test_bottomleft__del(self): + """Ensures the bottomleft attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.bottomleft + + def test_topright(self): + """Changing the topright attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_topright = (r.right + 20, r.top + 30) + expected_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.topright = new_topright + self.assertEqual(new_topright, r.topright) + self.assertEqual(expected_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_topright__invalid_value(self): + """Ensures the topright attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.topright = value + + def test_topright__del(self): + """Ensures the topright attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.topright + + def test_bottomright(self): + """Changing the bottomright attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_bottomright = (r.right + 20, r.bottom + 30) + expected_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.bottomright = new_bottomright + self.assertEqual(new_bottomright, r.bottomright) + self.assertEqual(expected_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_bottomright__invalid_value(self): + """Ensures the bottomright attribute handles invalid values correctly. + """ + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.bottomright = value + + def test_bottomright__del(self): + """Ensures the bottomright attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.bottomright + + def test_center(self): + """Changing the center attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_center = (r.centerx + 20, r.centery + 30) + expected_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.center = new_center + self.assertEqual(new_center, r.center) + self.assertEqual(expected_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_center__invalid_value(self): + """Ensures the center attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.center = value + + def test_center__del(self): + """Ensures the center attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.center + + def test_midleft(self): + """Changing the midleft attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_midleft = (r.left + 20, r.centery + 30) + expected_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.midleft = new_midleft + self.assertEqual(new_midleft, r.midleft) + self.assertEqual(expected_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_midleft__invalid_value(self): + """Ensures the midleft attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.midleft = value + + def test_midleft__del(self): + """Ensures the midleft attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.midleft + + def test_midright(self): + """Changing the midright attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_midright = (r.right + 20, r.centery + 30) + expected_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.midright = new_midright + self.assertEqual(new_midright, r.midright) + self.assertEqual(expected_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_midright__invalid_value(self): + """Ensures the midright attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.midright = value + + def test_midright__del(self): + """Ensures the midright attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.midright + + def test_midtop(self): + """Changing the midtop attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_midtop = (r.centerx + 20, r.top + 30) + expected_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.midtop = new_midtop + self.assertEqual(new_midtop, r.midtop) + self.assertEqual(expected_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_midtop__invalid_value(self): + """Ensures the midtop attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.midtop = value + + def test_midtop__del(self): + """Ensures the midtop attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.midtop + + def test_midbottom(self): + """Changing the midbottom attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_midbottom = (r.centerx + 20, r.bottom + 30) + expected_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.midbottom = new_midbottom + self.assertEqual(new_midbottom, r.midbottom) + self.assertEqual(expected_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_midbottom__invalid_value(self): + """Ensures the midbottom attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.midbottom = value + + def test_midbottom__del(self): + """Ensures the midbottom attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.midbottom + + def test_width(self): + """Changing the width resizes the rect from the top-left corner + """ + r = Rect(1, 2, 3, 4) + new_width = 10 + old_topleft = r.topleft + old_height = r.height + + r.width = new_width + self.assertEqual(new_width, r.width) + self.assertEqual(old_height, r.height) + self.assertEqual(old_topleft, r.topleft) + + def test_width__invalid_value(self): + """Ensures the width attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.width = value + + def test_width__del(self): + """Ensures the width attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.width + + def test_height(self): + """Changing the height resizes the rect from the top-left corner + """ + r = Rect(1, 2, 3, 4) + new_height = 10 + old_topleft = r.topleft + old_width = r.width + + r.height = new_height + self.assertEqual(new_height, r.height) + self.assertEqual(old_width, r.width) + self.assertEqual(old_topleft, r.topleft) + + def test_height__invalid_value(self): + """Ensures the height attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.height = value + + def test_height__del(self): + """Ensures the height attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.height + + def test_size(self): + """Changing the size resizes the rect from the top-left corner + """ + r = Rect(1, 2, 3, 4) + new_size = (10, 20) + old_topleft = r.topleft + + r.size = new_size + self.assertEqual(new_size, r.size) + self.assertEqual(old_topleft, r.topleft) + + def test_size__invalid_value(self): + """Ensures the size attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.size = value + + def test_size__del(self): + """Ensures the size attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.size + + def test_contains(self): + r = Rect(1, 2, 3, 4) + + self.assertTrue( + r.contains(Rect(2, 3, 1, 1)), "r does not contain Rect(2, 3, 1, 1)" + ) + self.assertTrue( + r.contains(Rect(r)), "r does not contain the same rect as itself" + ) + self.assertTrue( + r.contains(Rect(2, 3, 0, 0)), + "r does not contain an empty rect within its bounds", + ) + self.assertFalse(r.contains(Rect(0, 0, 1, 2)), "r contains Rect(0, 0, 1, 2)") + self.assertFalse(r.contains(Rect(4, 6, 1, 1)), "r contains Rect(4, 6, 1, 1)") + self.assertFalse(r.contains(Rect(4, 6, 0, 0)), "r contains Rect(4, 6, 0, 0)") + + def test_collidepoint(self): + r = Rect(1, 2, 3, 4) + + self.assertTrue( + r.collidepoint(r.left, r.top), "r does not collide with point (left, top)" + ) + self.assertFalse( + r.collidepoint(r.left - 1, r.top), "r collides with point (left - 1, top)" + ) + self.assertFalse( + r.collidepoint(r.left, r.top - 1), "r collides with point (left, top - 1)" + ) + self.assertFalse( + r.collidepoint(r.left - 1, r.top - 1), + "r collides with point (left - 1, top - 1)", + ) + + self.assertTrue( + r.collidepoint(r.right - 1, r.bottom - 1), + "r does not collide with point (right - 1, bottom - 1)", + ) + self.assertFalse( + r.collidepoint(r.right, r.bottom), "r collides with point (right, bottom)" + ) + self.assertFalse( + r.collidepoint(r.right - 1, r.bottom), + "r collides with point (right - 1, bottom)", + ) + self.assertFalse( + r.collidepoint(r.right, r.bottom - 1), + "r collides with point (right, bottom - 1)", + ) + + def test_inflate__larger(self): + """The inflate method inflates around the center of the rectangle + """ + r = Rect(2, 4, 6, 8) + r2 = r.inflate(4, 6) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left - 2, r2.left) + self.assertEqual(r.top - 3, r2.top) + self.assertEqual(r.right + 2, r2.right) + self.assertEqual(r.bottom + 3, r2.bottom) + self.assertEqual(r.width + 4, r2.width) + self.assertEqual(r.height + 6, r2.height) + + def test_inflate__smaller(self): + """The inflate method inflates around the center of the rectangle + """ + r = Rect(2, 4, 6, 8) + r2 = r.inflate(-4, -6) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left + 2, r2.left) + self.assertEqual(r.top + 3, r2.top) + self.assertEqual(r.right - 2, r2.right) + self.assertEqual(r.bottom - 3, r2.bottom) + self.assertEqual(r.width - 4, r2.width) + self.assertEqual(r.height - 6, r2.height) + + def test_inflate_ip__larger(self): + """The inflate_ip method inflates around the center of the rectangle + """ + r = Rect(2, 4, 6, 8) + r2 = Rect(r) + r2.inflate_ip(-4, -6) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left + 2, r2.left) + self.assertEqual(r.top + 3, r2.top) + self.assertEqual(r.right - 2, r2.right) + self.assertEqual(r.bottom - 3, r2.bottom) + self.assertEqual(r.width - 4, r2.width) + self.assertEqual(r.height - 6, r2.height) + + def test_inflate_ip__smaller(self): + """The inflate method inflates around the center of the rectangle + """ + r = Rect(2, 4, 6, 8) + r2 = Rect(r) + r2.inflate_ip(-4, -6) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left + 2, r2.left) + self.assertEqual(r.top + 3, r2.top) + self.assertEqual(r.right - 2, r2.right) + self.assertEqual(r.bottom - 3, r2.bottom) + self.assertEqual(r.width - 4, r2.width) + self.assertEqual(r.height - 6, r2.height) + + def test_clamp(self): + r = Rect(10, 10, 10, 10) + c = Rect(19, 12, 5, 5).clamp(r) + self.assertEqual(c.right, r.right) + self.assertEqual(c.top, 12) + c = Rect(1, 2, 3, 4).clamp(r) + self.assertEqual(c.topleft, r.topleft) + c = Rect(5, 500, 22, 33).clamp(r) + self.assertEqual(c.center, r.center) + + def test_clamp_ip(self): + r = Rect(10, 10, 10, 10) + c = Rect(19, 12, 5, 5) + c.clamp_ip(r) + self.assertEqual(c.right, r.right) + self.assertEqual(c.top, 12) + c = Rect(1, 2, 3, 4) + c.clamp_ip(r) + self.assertEqual(c.topleft, r.topleft) + c = Rect(5, 500, 22, 33) + c.clamp_ip(r) + self.assertEqual(c.center, r.center) + + def test_clip(self): + r1 = Rect(1, 2, 3, 4) + self.assertEqual(Rect(1, 2, 2, 2), r1.clip(Rect(0, 0, 3, 4))) + self.assertEqual(Rect(2, 2, 2, 4), r1.clip(Rect(2, 2, 10, 20))) + self.assertEqual(Rect(2, 3, 1, 2), r1.clip(Rect(2, 3, 1, 2))) + self.assertEqual((0, 0), r1.clip(20, 30, 5, 6).size) + self.assertEqual( + r1, r1.clip(Rect(r1)), "r1 does not clip an identical rect to itself" + ) + + def test_clipline(self): + """Ensures clipline handles four int parameters. + + Tests the clipline(x1, y1, x2, y2) format. + """ + rect = Rect((1, 2), (35, 40)) + x1 = 5 + y1 = 6 + x2 = 11 + y2 = 19 + expected_line = ((x1, y1), (x2, y2)) + + clipped_line = rect.clipline(x1, y1, x2, y2) + + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__two_sequences(self): + """Ensures clipline handles a sequence of two sequences. + + Tests the clipline((x1, y1), (x2, y2)) format. + Tests the sequences as different types. + """ + rect = Rect((1, 2), (35, 40)) + pt1 = (5, 6) + pt2 = (11, 19) + + INNER_SEQUENCES = (list, tuple, Vector2) + expected_line = (pt1, pt2) + + for inner_seq1 in INNER_SEQUENCES: + endpt1 = inner_seq1(pt1) + + for inner_seq2 in INNER_SEQUENCES: + clipped_line = rect.clipline((endpt1, inner_seq2(pt2))) + + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__sequence_of_four_ints(self): + """Ensures clipline handles a sequence of four ints. + + Tests the clipline((x1, y1, x2, y2)) format. + Tests the sequence as different types. + """ + rect = Rect((1, 2), (35, 40)) + line = (5, 6, 11, 19) + expected_line = ((line[0], line[1]), (line[2], line[3])) + + for outer_seq in (list, tuple): + clipped_line = rect.clipline(outer_seq(line)) + + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__sequence_of_two_sequences(self): + """Ensures clipline handles a sequence of two sequences. + + Tests the clipline(((x1, y1), (x2, y2))) format. + Tests the sequences as different types. + """ + rect = Rect((1, 2), (35, 40)) + pt1 = (5, 6) + pt2 = (11, 19) + + INNER_SEQUENCES = (list, tuple, Vector2) + expected_line = (pt1, pt2) + + for inner_seq1 in INNER_SEQUENCES: + endpt1 = inner_seq1(pt1) + + for inner_seq2 in INNER_SEQUENCES: + endpt2 = inner_seq2(pt2) + + for outer_seq in (list, tuple): + clipped_line = rect.clipline(outer_seq((endpt1, endpt2))) + + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__floats(self): + """Ensures clipline handles float parameters.""" + rect = Rect((1, 2), (35, 40)) + x1 = 5.9 + y1 = 6.9 + x2 = 11.9 + y2 = 19.9 + + # Floats are truncated. + expected_line = ( + (math.floor(x1), math.floor(y1)), + (math.floor(x2), math.floor(y2)), + ) + + clipped_line = rect.clipline(x1, y1, x2, y2) + + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__no_overlap(self): + """Ensures lines that do not overlap the rect are not clipped.""" + rect = Rect((10, 25), (15, 20)) + # Use a bigger rect to help create test lines. + big_rect = rect.inflate(2, 2) + lines = ( + (big_rect.bottomleft, big_rect.topleft), # Left edge. + (big_rect.topleft, big_rect.topright), # Top edge. + (big_rect.topright, big_rect.bottomright), # Right edge. + (big_rect.bottomright, big_rect.bottomleft), + ) # Bottom edge. + expected_line = () + + # Test lines outside rect. + for line in lines: + clipped_line = rect.clipline(line) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__both_endpoints_outside(self): + """Ensures lines that overlap the rect are clipped. + + Testing lines with both endpoints outside the rect. + """ + rect = Rect((0, 0), (20, 20)) + # Use a bigger rect to help create test lines. + big_rect = rect.inflate(2, 2) + + # Create a dict of lines and expected results. + line_dict = { + (big_rect.midleft, big_rect.midright): ( + rect.midleft, + (rect.midright[0] - 1, rect.midright[1]), + ), + (big_rect.midtop, big_rect.midbottom): ( + rect.midtop, + (rect.midbottom[0], rect.midbottom[1] - 1), + ), + # Diagonals. + (big_rect.topleft, big_rect.bottomright): ( + rect.topleft, + (rect.bottomright[0] - 1, rect.bottomright[1] - 1), + ), + # This line needs a small adjustment to make sure it intersects + # the rect correctly. + ( + (big_rect.topright[0] - 1, big_rect.topright[1]), + (big_rect.bottomleft[0], big_rect.bottomleft[1] - 1), + ): ( + (rect.topright[0] - 1, rect.topright[1]), + (rect.bottomleft[0], rect.bottomleft[1] - 1), + ), + } + + for line, expected_line in line_dict.items(): + clipped_line = rect.clipline(line) + + self.assertTupleEqual(clipped_line, expected_line) + + # Swap endpoints to test for symmetry. + expected_line = (expected_line[1], expected_line[0]) + + clipped_line = rect.clipline((line[1], line[0])) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__both_endpoints_inside(self): + """Ensures lines that overlap the rect are clipped. + + Testing lines with both endpoints inside the rect. + """ + rect = Rect((-10, -5), (20, 20)) + # Use a smaller rect to help create test lines. + small_rect = rect.inflate(-2, -2) + + lines = ( + (small_rect.midleft, small_rect.midright), + (small_rect.midtop, small_rect.midbottom), + # Diagonals. + (small_rect.topleft, small_rect.bottomright), + (small_rect.topright, small_rect.bottomleft), + ) + + for line in lines: + expected_line = line + + clipped_line = rect.clipline(line) + + self.assertTupleEqual(clipped_line, expected_line) + + # Swap endpoints to test for symmetry. + expected_line = (expected_line[1], expected_line[0]) + + clipped_line = rect.clipline((line[1], line[0])) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__endpoints_inside_and_outside(self): + """Ensures lines that overlap the rect are clipped. + + Testing lines with one endpoint outside the rect and the other is + inside the rect. + """ + rect = Rect((0, 0), (21, 21)) + # Use a bigger rect to help create test lines. + big_rect = rect.inflate(2, 2) + + # Create a dict of lines and expected results. + line_dict = { + (big_rect.midleft, rect.center): (rect.midleft, rect.center), + (big_rect.midtop, rect.center): (rect.midtop, rect.center), + (big_rect.midright, rect.center): ( + (rect.midright[0] - 1, rect.midright[1]), + rect.center, + ), + (big_rect.midbottom, rect.center): ( + (rect.midbottom[0], rect.midbottom[1] - 1), + rect.center, + ), + # Diagonals. + (big_rect.topleft, rect.center): (rect.topleft, rect.center), + (big_rect.topright, rect.center): ( + (rect.topright[0] - 1, rect.topright[1]), + rect.center, + ), + (big_rect.bottomright, rect.center): ( + (rect.bottomright[0] - 1, rect.bottomright[1] - 1), + rect.center, + ), + # This line needs a small adjustment to make sure it intersects + # the rect correctly. + ((big_rect.bottomleft[0], big_rect.bottomleft[1] - 1), rect.center): ( + (rect.bottomleft[0], rect.bottomleft[1] - 1), + rect.center, + ), + } + + for line, expected_line in line_dict.items(): + clipped_line = rect.clipline(line) + + self.assertTupleEqual(clipped_line, expected_line) + + # Swap endpoints to test for symmetry. + expected_line = (expected_line[1], expected_line[0]) + + clipped_line = rect.clipline((line[1], line[0])) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__edges(self): + """Ensures clipline properly clips line that are along the rect edges. + """ + rect = Rect((10, 25), (15, 20)) + + # Create a dict of edges and expected results. + edge_dict = { + # Left edge. + (rect.bottomleft, rect.topleft): ( + (rect.bottomleft[0], rect.bottomleft[1] - 1), + rect.topleft, + ), + # Top edge. + (rect.topleft, rect.topright): ( + rect.topleft, + (rect.topright[0] - 1, rect.topright[1]), + ), + # Right edge. + (rect.topright, rect.bottomright): (), + # Bottom edge. + (rect.bottomright, rect.bottomleft): (), + } + + for edge, expected_line in edge_dict.items(): + clipped_line = rect.clipline(edge) + + self.assertTupleEqual(clipped_line, expected_line) + + # Swap endpoints to test for symmetry. + if expected_line: + expected_line = (expected_line[1], expected_line[0]) + + clipped_line = rect.clipline((edge[1], edge[0])) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__equal_endpoints_with_overlap(self): + """Ensures clipline handles lines with both endpoints the same. + + Testing lines that overlap the rect. + """ + rect = Rect((10, 25), (15, 20)) + + # Test all the points in and on a rect. + pts = ( + (x, y) + for x in range(rect.left, rect.right) + for y in range(rect.top, rect.bottom) + ) + + for pt in pts: + expected_line = (pt, pt) + + clipped_line = rect.clipline((pt, pt)) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__equal_endpoints_no_overlap(self): + """Ensures clipline handles lines with both endpoints the same. + + Testing lines that do not overlap the rect. + """ + expected_line = () + rect = Rect((10, 25), (15, 20)) + + # Test points outside rect. + for pt in test_utils.rect_perimeter_pts(rect.inflate(2, 2)): + clipped_line = rect.clipline((pt, pt)) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__zero_size_rect(self): + """Ensures clipline handles zero sized rects correctly.""" + expected_line = () + + for size in ((0, 15), (15, 0), (0, 0)): + rect = Rect((10, 25), size) + + clipped_line = rect.clipline(rect.topleft, rect.topleft) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__negative_size_rect(self): + """Ensures clipline handles negative sized rects correctly.""" + expected_line = () + + for size in ((-15, 20), (15, -20), (-15, -20)): + rect = Rect((10, 25), size) + norm_rect = rect.copy() + norm_rect.normalize() + # Use a bigger rect to help create test lines. + big_rect = norm_rect.inflate(2, 2) + + # Create a dict of lines and expected results. Some line have both + # endpoints outside the rect and some have one inside and one + # outside. + line_dict = { + (big_rect.midleft, big_rect.midright): ( + norm_rect.midleft, + (norm_rect.midright[0] - 1, norm_rect.midright[1]), + ), + (big_rect.midtop, big_rect.midbottom): ( + norm_rect.midtop, + (norm_rect.midbottom[0], norm_rect.midbottom[1] - 1), + ), + (big_rect.midleft, norm_rect.center): ( + norm_rect.midleft, + norm_rect.center, + ), + (big_rect.midtop, norm_rect.center): ( + norm_rect.midtop, + norm_rect.center, + ), + (big_rect.midright, norm_rect.center): ( + (norm_rect.midright[0] - 1, norm_rect.midright[1]), + norm_rect.center, + ), + (big_rect.midbottom, norm_rect.center): ( + (norm_rect.midbottom[0], norm_rect.midbottom[1] - 1), + norm_rect.center, + ), + } + + for line, expected_line in line_dict.items(): + clipped_line = rect.clipline(line) + + # Make sure rect wasn't normalized. + self.assertNotEqual(rect, norm_rect) + self.assertTupleEqual(clipped_line, expected_line) + + # Swap endpoints to test for symmetry. + expected_line = (expected_line[1], expected_line[0]) + + clipped_line = rect.clipline((line[1], line[0])) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__invalid_line(self): + """Ensures clipline handles invalid lines correctly.""" + rect = Rect((0, 0), (10, 20)) + invalid_lines = ( + (), + (1,), + (1, 2), + (1, 2, 3), + (1, 2, 3, 4, 5), + ((1, 2),), + ((1, 2), (3,)), + ((1, 2), 3), + ((1, 2, 5), (3, 4)), + ((1, 2), (3, 4, 5)), + ((1, 2), (3, 4), (5, 6)), + ) + + for line in invalid_lines: + with self.assertRaises(TypeError): + clipped_line = rect.clipline(line) + + with self.assertRaises(TypeError): + clipped_line = rect.clipline(*line) + + def test_move(self): + r = Rect(1, 2, 3, 4) + move_x = 10 + move_y = 20 + r2 = r.move(move_x, move_y) + expected_r2 = Rect(r.left + move_x, r.top + move_y, r.width, r.height) + self.assertEqual(expected_r2, r2) + + def test_move_ip(self): + r = Rect(1, 2, 3, 4) + r2 = Rect(r) + move_x = 10 + move_y = 20 + r2.move_ip(move_x, move_y) + expected_r2 = Rect(r.left + move_x, r.top + move_y, r.width, r.height) + self.assertEqual(expected_r2, r2) + + def test_union(self): + r1 = Rect(1, 1, 1, 2) + r2 = Rect(-2, -2, 1, 2) + self.assertEqual(Rect(-2, -2, 4, 5), r1.union(r2)) + + def test_union__with_identical_Rect(self): + r1 = Rect(1, 2, 3, 4) + self.assertEqual(r1, r1.union(Rect(r1))) + + def test_union_ip(self): + r1 = Rect(1, 1, 1, 2) + r2 = Rect(-2, -2, 1, 2) + r1.union_ip(r2) + self.assertEqual(Rect(-2, -2, 4, 5), r1) + + def test_unionall(self): + r1 = Rect(0, 0, 1, 1) + r2 = Rect(-2, -2, 1, 1) + r3 = Rect(2, 2, 1, 1) + + r4 = r1.unionall([r2, r3]) + self.assertEqual(Rect(-2, -2, 5, 5), r4) + + def test_unionall__invalid_rect_format(self): + """Ensures unionall correctly handles invalid rect parameters.""" + numbers = [0, 1.2, 2, 3.3] + strs = ["a", "b", "c"] + nones = [None, None] + + for invalid_rects in (numbers, strs, nones): + with self.assertRaises(TypeError): + Rect(0, 0, 1, 1).unionall(invalid_rects) + + def test_unionall_ip(self): + r1 = Rect(0, 0, 1, 1) + r2 = Rect(-2, -2, 1, 1) + r3 = Rect(2, 2, 1, 1) + + r1.unionall_ip([r2, r3]) + self.assertEqual(Rect(-2, -2, 5, 5), r1) + + # Bug for an empty list. Would return a Rect instead of None. + self.assertTrue(r1.unionall_ip([]) is None) + + def test_unionall__invalid_rect_format(self): + """Ensures unionall_ip correctly handles invalid rect parameters.""" + numbers = [0, 1.2, 2, 3.3] + strs = ["a", "b", "c"] + nones = [None, None] + + for invalid_rects in (numbers, strs, nones): + with self.assertRaises(TypeError): + Rect(0, 0, 1, 1).unionall_ip(invalid_rects) + + def test_colliderect(self): + r1 = Rect(1, 2, 3, 4) + self.assertTrue( + r1.colliderect(Rect(0, 0, 2, 3)), + "r1 does not collide with Rect(0, 0, 2, 3)", + ) + self.assertFalse( + r1.colliderect(Rect(0, 0, 1, 2)), "r1 collides with Rect(0, 0, 1, 2)" + ) + self.assertFalse( + r1.colliderect(Rect(r1.right, r1.bottom, 2, 2)), + "r1 collides with Rect(r1.right, r1.bottom, 2, 2)", + ) + self.assertTrue( + r1.colliderect(Rect(r1.left + 1, r1.top + 1, r1.width - 2, r1.height - 2)), + "r1 does not collide with Rect(r1.left + 1, r1.top + 1, " + + "r1.width - 2, r1.height - 2)", + ) + self.assertTrue( + r1.colliderect(Rect(r1.left - 1, r1.top - 1, r1.width + 2, r1.height + 2)), + "r1 does not collide with Rect(r1.left - 1, r1.top - 1, " + + "r1.width + 2, r1.height + 2)", + ) + self.assertTrue( + r1.colliderect(Rect(r1)), "r1 does not collide with an identical rect" + ) + self.assertFalse( + r1.colliderect(Rect(r1.right, r1.bottom, 0, 0)), + "r1 collides with Rect(r1.right, r1.bottom, 0, 0)", + ) + self.assertFalse( + r1.colliderect(Rect(r1.right, r1.bottom, 1, 1)), + "r1 collides with Rect(r1.right, r1.bottom, 1, 1)", + ) + + def testEquals(self): + """ check to see how the rect uses __eq__ + """ + r1 = Rect(1, 2, 3, 4) + r2 = Rect(10, 20, 30, 40) + r3 = (10, 20, 30, 40) + r4 = Rect(10, 20, 30, 40) + + class foo(Rect): + def __eq__(self, other): + return id(self) == id(other) + + def __ne__(self, other): + return id(self) != id(other) + + class foo2(Rect): + pass + + r5 = foo(10, 20, 30, 40) + r6 = foo2(10, 20, 30, 40) + + self.assertNotEqual(r5, r2) + + # because we define equality differently for this subclass. + self.assertEqual(r6, r2) + + rect_list = [r1, r2, r3, r4, r6] + + # see if we can remove 4 of these. + rect_list.remove(r2) + rect_list.remove(r2) + rect_list.remove(r2) + rect_list.remove(r2) + self.assertRaises(ValueError, rect_list.remove, r2) + + def test_collidedict(self): + """Ensures collidedict detects collisions.""" + rect = Rect(1, 1, 10, 10) + + collide_item1 = ("collide 1", rect.copy()) + collide_item2 = ("collide 2", Rect(5, 5, 10, 10)) + no_collide_item1 = ("no collide 1", Rect(60, 60, 10, 10)) + no_collide_item2 = ("no collide 2", Rect(70, 70, 10, 10)) + + # Dict to check collisions with values. + rect_values = dict( + (collide_item1, collide_item2, no_collide_item1, no_collide_item2) + ) + value_collide_items = (collide_item1, collide_item2) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = tuple((tuple(v), k) for k, v in value_collide_items) + + for use_values in (True, False): + if use_values: + expected_items = value_collide_items + d = rect_values + else: + expected_items = key_collide_items + d = rect_keys + + collide_item = rect.collidedict(d, use_values) + + # The detected collision could be any of the possible items. + self.assertIn(collide_item, expected_items) + + def test_collidedict__no_collision(self): + """Ensures collidedict returns None when no collisions.""" + rect = Rect(1, 1, 10, 10) + + no_collide_item1 = ("no collide 1", Rect(50, 50, 10, 10)) + no_collide_item2 = ("no collide 2", Rect(60, 60, 10, 10)) + no_collide_item3 = ("no collide 3", Rect(70, 70, 10, 10)) + + # Dict to check collisions with values. + rect_values = dict((no_collide_item1, no_collide_item2, no_collide_item3)) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + collide_item = rect.collidedict(d, use_values) + + self.assertIsNone(collide_item) + + def test_collidedict__barely_touching(self): + """Ensures collidedict works correctly for rects that barely touch.""" + rect = Rect(1, 1, 10, 10) + # Small rect to test barely touching collisions. + collide_rect = Rect(0, 0, 1, 1) + + collide_item1 = ("collide 1", collide_rect) + no_collide_item1 = ("no collide 1", Rect(50, 50, 10, 10)) + no_collide_item2 = ("no collide 2", Rect(60, 60, 10, 10)) + no_collide_item3 = ("no collide 3", Rect(70, 70, 10, 10)) + + # Dict to check collisions with values. + no_collide_rect_values = dict( + (no_collide_item1, no_collide_item2, no_collide_item3) + ) + + # Dict to check collisions with keys. + no_collide_rect_keys = {tuple(v): k for k, v in no_collide_rect_values.items()} + + # Tests the collide_rect on each of the rect's corners. + for attr in ("topleft", "topright", "bottomright", "bottomleft"): + setattr(collide_rect, attr, getattr(rect, attr)) + + for use_values in (True, False): + if use_values: + expected_item = collide_item1 + d = dict(no_collide_rect_values) + else: + expected_item = (tuple(collide_item1[1]), collide_item1[0]) + d = dict(no_collide_rect_keys) + + d.update((expected_item,)) # Add in the expected item. + + collide_item = rect.collidedict(d, use_values) + + self.assertTupleEqual(collide_item, expected_item) + + def test_collidedict__zero_sized_rects(self): + """Ensures collidedict works correctly with zero sized rects. + + There should be no collisions with zero sized rects. + """ + zero_rect1 = Rect(1, 1, 0, 0) + zero_rect2 = Rect(1, 1, 1, 0) + zero_rect3 = Rect(1, 1, 0, 1) + zero_rect4 = Rect(1, 1, -1, 0) + zero_rect5 = Rect(1, 1, 0, -1) + + no_collide_item1 = ("no collide 1", zero_rect1.copy()) + no_collide_item2 = ("no collide 2", zero_rect2.copy()) + no_collide_item3 = ("no collide 3", zero_rect3.copy()) + no_collide_item4 = ("no collide 4", zero_rect4.copy()) + no_collide_item5 = ("no collide 5", zero_rect5.copy()) + no_collide_item6 = ("no collide 6", Rect(0, 0, 10, 10)) + no_collide_item7 = ("no collide 7", Rect(0, 0, 2, 2)) + + # Dict to check collisions with values. + rect_values = dict( + ( + no_collide_item1, + no_collide_item2, + no_collide_item3, + no_collide_item4, + no_collide_item5, + no_collide_item6, + no_collide_item7, + ) + ) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + for zero_rect in ( + zero_rect1, + zero_rect2, + zero_rect3, + zero_rect4, + zero_rect5, + ): + collide_item = zero_rect.collidedict(d, use_values) + + self.assertIsNone(collide_item) + + def test_collidedict__zero_sized_rects_as_args(self): + """Ensures collidedict works correctly with zero sized rects as args. + + There should be no collisions with zero sized rects. + """ + rect = Rect(0, 0, 10, 10) + + no_collide_item1 = ("no collide 1", Rect(1, 1, 0, 0)) + no_collide_item2 = ("no collide 2", Rect(1, 1, 1, 0)) + no_collide_item3 = ("no collide 3", Rect(1, 1, 0, 1)) + + # Dict to check collisions with values. + rect_values = dict((no_collide_item1, no_collide_item2, no_collide_item3)) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + collide_item = rect.collidedict(d, use_values) + + self.assertIsNone(collide_item) + + def test_collidedict__negative_sized_rects(self): + """Ensures collidedict works correctly with negative sized rects.""" + neg_rect = Rect(1, 1, -1, -1) + + collide_item1 = ("collide 1", neg_rect.copy()) + collide_item2 = ("collide 2", Rect(0, 0, 10, 10)) + no_collide_item1 = ("no collide 1", Rect(1, 1, 10, 10)) + + # Dict to check collisions with values. + rect_values = dict((collide_item1, collide_item2, no_collide_item1)) + value_collide_items = (collide_item1, collide_item2) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = tuple((tuple(v), k) for k, v in value_collide_items) + + for use_values in (True, False): + if use_values: + collide_items = value_collide_items + d = rect_values + else: + collide_items = key_collide_items + d = rect_keys + + collide_item = neg_rect.collidedict(d, use_values) + + # The detected collision could be any of the possible items. + self.assertIn(collide_item, collide_items) + + def test_collidedict__negative_sized_rects_as_args(self): + """Ensures collidedict works correctly with negative sized rect args. + """ + rect = Rect(0, 0, 10, 10) + + collide_item1 = ("collide 1", Rect(1, 1, -1, -1)) + no_collide_item1 = ("no collide 1", Rect(1, 1, -1, 0)) + no_collide_item2 = ("no collide 2", Rect(1, 1, 0, -1)) + + # Dict to check collisions with values. + rect_values = dict((collide_item1, no_collide_item1, no_collide_item2)) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + for use_values in (True, False): + if use_values: + expected_item = collide_item1 + d = rect_values + else: + expected_item = (tuple(collide_item1[1]), collide_item1[0]) + d = rect_keys + + collide_item = rect.collidedict(d, use_values) + + self.assertTupleEqual(collide_item, expected_item) + + def test_collidedict__invalid_dict_format(self): + """Ensures collidedict correctly handles invalid dict parameters.""" + rect = Rect(0, 0, 10, 10) + + invalid_value_dict = ("collide", rect.copy()) + invalid_key_dict = tuple(invalid_value_dict[1]), invalid_value_dict[0] + + for use_values in (True, False): + d = invalid_value_dict if use_values else invalid_key_dict + + with self.assertRaises(TypeError): + collide_item = rect.collidedict(d, use_values) + + def test_collidedict__invalid_dict_value_format(self): + """Ensures collidedict correctly handles dicts with invalid values.""" + rect = Rect(0, 0, 10, 10) + rect_keys = {tuple(rect): "collide"} + + with self.assertRaises(TypeError): + collide_item = rect.collidedict(rect_keys, 1) + + def test_collidedict__invalid_dict_key_format(self): + """Ensures collidedict correctly handles dicts with invalid keys.""" + rect = Rect(0, 0, 10, 10) + rect_values = {"collide": rect.copy()} + + with self.assertRaises(TypeError): + collide_item = rect.collidedict(rect_values) + + def test_collidedict__invalid_use_values_format(self): + """Ensures collidedict correctly handles invalid use_values parameters. + """ + rect = Rect(0, 0, 1, 1) + d = {} + + for invalid_param in (None, d, 1.1): + with self.assertRaises(TypeError): + collide_item = rect.collidedict(d, invalid_param) + + def test_collidedictall(self): + """Ensures collidedictall detects collisions.""" + rect = Rect(1, 1, 10, 10) + + collide_item1 = ("collide 1", rect.copy()) + collide_item2 = ("collide 2", Rect(5, 5, 10, 10)) + no_collide_item1 = ("no collide 1", Rect(60, 60, 20, 20)) + no_collide_item2 = ("no collide 2", Rect(70, 70, 20, 20)) + + # Dict to check collisions with values. + rect_values = dict( + (collide_item1, collide_item2, no_collide_item1, no_collide_item2) + ) + value_collide_items = [collide_item1, collide_item2] + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = [(tuple(v), k) for k, v in value_collide_items] + + for use_values in (True, False): + if use_values: + expected_items = value_collide_items + d = rect_values + else: + expected_items = key_collide_items + d = rect_keys + + collide_items = rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__no_collision(self): + """Ensures collidedictall returns an empty list when no collisions.""" + rect = Rect(1, 1, 10, 10) + + no_collide_item1 = ("no collide 1", Rect(50, 50, 20, 20)) + no_collide_item2 = ("no collide 2", Rect(60, 60, 20, 20)) + no_collide_item3 = ("no collide 3", Rect(70, 70, 20, 20)) + + # Dict to check collisions with values. + rect_values = dict((no_collide_item1, no_collide_item2, no_collide_item3)) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + expected_items = [] + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + collide_items = rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__barely_touching(self): + """Ensures collidedictall works correctly for rects that barely touch. + """ + rect = Rect(1, 1, 10, 10) + # Small rect to test barely touching collisions. + collide_rect = Rect(0, 0, 1, 1) + + collide_item1 = ("collide 1", collide_rect) + no_collide_item1 = ("no collide 1", Rect(50, 50, 20, 20)) + no_collide_item2 = ("no collide 2", Rect(60, 60, 20, 20)) + no_collide_item3 = ("no collide 3", Rect(70, 70, 20, 20)) + + # Dict to check collisions with values. + no_collide_rect_values = dict( + (no_collide_item1, no_collide_item2, no_collide_item3) + ) + + # Dict to check collisions with keys. + no_collide_rect_keys = {tuple(v): k for k, v in no_collide_rect_values.items()} + + # Tests the collide_rect on each of the rect's corners. + for attr in ("topleft", "topright", "bottomright", "bottomleft"): + setattr(collide_rect, attr, getattr(rect, attr)) + + for use_values in (True, False): + if use_values: + expected_items = [collide_item1] + d = dict(no_collide_rect_values) + else: + expected_items = [(tuple(collide_item1[1]), collide_item1[0])] + d = dict(no_collide_rect_keys) + + d.update(expected_items) # Add in the expected items. + + collide_items = rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__zero_sized_rects(self): + """Ensures collidedictall works correctly with zero sized rects. + + There should be no collisions with zero sized rects. + """ + zero_rect1 = Rect(2, 2, 0, 0) + zero_rect2 = Rect(2, 2, 2, 0) + zero_rect3 = Rect(2, 2, 0, 2) + zero_rect4 = Rect(2, 2, -2, 0) + zero_rect5 = Rect(2, 2, 0, -2) + + no_collide_item1 = ("no collide 1", zero_rect1.copy()) + no_collide_item2 = ("no collide 2", zero_rect2.copy()) + no_collide_item3 = ("no collide 3", zero_rect3.copy()) + no_collide_item4 = ("no collide 4", zero_rect4.copy()) + no_collide_item5 = ("no collide 5", zero_rect5.copy()) + no_collide_item6 = ("no collide 6", Rect(0, 0, 10, 10)) + no_collide_item7 = ("no collide 7", Rect(0, 0, 2, 2)) + + # Dict to check collisions with values. + rect_values = dict( + ( + no_collide_item1, + no_collide_item2, + no_collide_item3, + no_collide_item4, + no_collide_item5, + no_collide_item6, + no_collide_item7, + ) + ) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + expected_items = [] + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + for zero_rect in ( + zero_rect1, + zero_rect2, + zero_rect3, + zero_rect4, + zero_rect5, + ): + collide_items = zero_rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__zero_sized_rects_as_args(self): + """Ensures collidedictall works correctly with zero sized rects + as args. + + There should be no collisions with zero sized rects. + """ + rect = Rect(0, 0, 20, 20) + + no_collide_item1 = ("no collide 1", Rect(2, 2, 0, 0)) + no_collide_item2 = ("no collide 2", Rect(2, 2, 2, 0)) + no_collide_item3 = ("no collide 3", Rect(2, 2, 0, 2)) + + # Dict to check collisions with values. + rect_values = dict((no_collide_item1, no_collide_item2, no_collide_item3)) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + expected_items = [] + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + collide_items = rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__negative_sized_rects(self): + """Ensures collidedictall works correctly with negative sized rects.""" + neg_rect = Rect(2, 2, -2, -2) + + collide_item1 = ("collide 1", neg_rect.copy()) + collide_item2 = ("collide 2", Rect(0, 0, 20, 20)) + no_collide_item1 = ("no collide 1", Rect(2, 2, 20, 20)) + + # Dict to check collisions with values. + rect_values = dict((collide_item1, collide_item2, no_collide_item1)) + value_collide_items = [collide_item1, collide_item2] + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = [(tuple(v), k) for k, v in value_collide_items] + + for use_values in (True, False): + if use_values: + expected_items = value_collide_items + d = rect_values + else: + expected_items = key_collide_items + d = rect_keys + + collide_items = neg_rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__negative_sized_rects_as_args(self): + """Ensures collidedictall works correctly with negative sized rect + args. + """ + rect = Rect(0, 0, 10, 10) + + collide_item1 = ("collide 1", Rect(1, 1, -1, -1)) + no_collide_item1 = ("no collide 1", Rect(1, 1, -1, 0)) + no_collide_item2 = ("no collide 2", Rect(1, 1, 0, -1)) + + # Dict to check collisions with values. + rect_values = dict((collide_item1, no_collide_item1, no_collide_item2)) + value_collide_items = [collide_item1] + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = [(tuple(v), k) for k, v in value_collide_items] + + for use_values in (True, False): + if use_values: + expected_items = value_collide_items + d = rect_values + else: + expected_items = key_collide_items + d = rect_keys + + collide_items = rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__invalid_dict_format(self): + """Ensures collidedictall correctly handles invalid dict parameters.""" + rect = Rect(0, 0, 10, 10) + + invalid_value_dict = ("collide", rect.copy()) + invalid_key_dict = tuple(invalid_value_dict[1]), invalid_value_dict[0] + + for use_values in (True, False): + d = invalid_value_dict if use_values else invalid_key_dict + + with self.assertRaises(TypeError): + collide_item = rect.collidedictall(d, use_values) + + def test_collidedictall__invalid_dict_value_format(self): + """Ensures collidedictall correctly handles dicts with invalid values. + """ + rect = Rect(0, 0, 10, 10) + rect_keys = {tuple(rect): "collide"} + + with self.assertRaises(TypeError): + collide_items = rect.collidedictall(rect_keys, 1) + + def test_collidedictall__invalid_dict_key_format(self): + """Ensures collidedictall correctly handles dicts with invalid keys.""" + rect = Rect(0, 0, 10, 10) + rect_values = {"collide": rect.copy()} + + with self.assertRaises(TypeError): + collide_items = rect.collidedictall(rect_values) + + def test_collidedictall__invalid_use_values_format(self): + """Ensures collidedictall correctly handles invalid use_values + parameters. + """ + rect = Rect(0, 0, 1, 1) + d = {} + + for invalid_param in (None, d, 1.1): + with self.assertRaises(TypeError): + collide_items = rect.collidedictall(d, invalid_param) + + def test_collidelist(self): + + # __doc__ (as of 2008-08-02) for pygame.rect.Rect.collidelist: + + # Rect.collidelist(list): return index + # test if one rectangle in a list intersects + # + # Test whether the rectangle collides with any in a sequence of + # rectangles. The index of the first collision found is returned. If + # no collisions are found an index of -1 is returned. + + r = Rect(1, 1, 10, 10) + l = [Rect(50, 50, 1, 1), Rect(5, 5, 10, 10), Rect(15, 15, 1, 1)] + + self.assertEqual(r.collidelist(l), 1) + + f = [Rect(50, 50, 1, 1), (100, 100, 4, 4)] + self.assertEqual(r.collidelist(f), -1) + + def test_collidelistall(self): + + # __doc__ (as of 2008-08-02) for pygame.rect.Rect.collidelistall: + + # Rect.collidelistall(list): return indices + # test if all rectangles in a list intersect + # + # Returns a list of all the indices that contain rectangles that + # collide with the Rect. If no intersecting rectangles are found, an + # empty list is returned. + + r = Rect(1, 1, 10, 10) + + l = [ + Rect(1, 1, 10, 10), + Rect(5, 5, 10, 10), + Rect(15, 15, 1, 1), + Rect(2, 2, 1, 1), + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [Rect(50, 50, 1, 1), Rect(20, 20, 5, 5)] + self.assertFalse(r.collidelistall(f)) + + def test_fit(self): + + # __doc__ (as of 2008-08-02) for pygame.rect.Rect.fit: + + # Rect.fit(Rect): return Rect + # resize and move a rectangle with aspect ratio + # + # Returns a new rectangle that is moved and resized to fit another. + # The aspect ratio of the original Rect is preserved, so the new + # rectangle may be smaller than the target in either width or height. + + r = Rect(10, 10, 30, 30) + + r2 = Rect(30, 30, 15, 10) + + f = r.fit(r2) + self.assertTrue(r2.contains(f)) + + f2 = r2.fit(r) + self.assertTrue(r.contains(f2)) + + def test_copy(self): + r = Rect(1, 2, 10, 20) + c = r.copy() + self.assertEqual(c, r) + + def test_subscript(self): + r = Rect(1, 2, 3, 4) + self.assertEqual(r[0], 1) + self.assertEqual(r[1], 2) + self.assertEqual(r[2], 3) + self.assertEqual(r[3], 4) + self.assertEqual(r[-1], 4) + self.assertEqual(r[-2], 3) + self.assertEqual(r[-4], 1) + self.assertRaises(IndexError, r.__getitem__, 5) + self.assertRaises(IndexError, r.__getitem__, -5) + self.assertEqual(r[0:2], [1, 2]) + self.assertEqual(r[0:4], [1, 2, 3, 4]) + self.assertEqual(r[0:-1], [1, 2, 3]) + self.assertEqual(r[:], [1, 2, 3, 4]) + self.assertEqual(r[...], [1, 2, 3, 4]) + self.assertEqual(r[0:4:2], [1, 3]) + self.assertEqual(r[0:4:3], [1, 4]) + self.assertEqual(r[3::-1], [4, 3, 2, 1]) + self.assertRaises(TypeError, r.__getitem__, None) + + def test_ass_subscript(self): + r = Rect(0, 0, 0, 0) + r[...] = 1, 2, 3, 4 + self.assertEqual(r, [1, 2, 3, 4]) + self.assertRaises(TypeError, r.__setitem__, None, 0) + self.assertEqual(r, [1, 2, 3, 4]) + self.assertRaises(TypeError, r.__setitem__, 0, "") + self.assertEqual(r, [1, 2, 3, 4]) + self.assertRaises(IndexError, r.__setitem__, 4, 0) + self.assertEqual(r, [1, 2, 3, 4]) + self.assertRaises(IndexError, r.__setitem__, -5, 0) + self.assertEqual(r, [1, 2, 3, 4]) + r[0] = 10 + self.assertEqual(r, [10, 2, 3, 4]) + r[3] = 40 + self.assertEqual(r, [10, 2, 3, 40]) + r[-1] = 400 + self.assertEqual(r, [10, 2, 3, 400]) + r[-4] = 100 + self.assertEqual(r, [100, 2, 3, 400]) + r[1:3] = 0 + self.assertEqual(r, [100, 0, 0, 400]) + r[...] = 0 + self.assertEqual(r, [0, 0, 0, 0]) + r[:] = 9 + self.assertEqual(r, [9, 9, 9, 9]) + r[:] = 11, 12, 13, 14 + self.assertEqual(r, [11, 12, 13, 14]) + r[::-1] = r + self.assertEqual(r, [14, 13, 12, 11]) + + +class SubclassTest(unittest.TestCase): + class MyRect(Rect): + def __init__(self, *args, **kwds): + super(SubclassTest.MyRect, self).__init__(*args, **kwds) + self.an_attribute = True + + def test_copy(self): + mr1 = self.MyRect(1, 2, 10, 20) + self.assertTrue(mr1.an_attribute) + mr2 = mr1.copy() + self.assertTrue(isinstance(mr2, self.MyRect)) + self.assertRaises(AttributeError, getattr, mr2, "an_attribute") + + def test_move(self): + mr1 = self.MyRect(1, 2, 10, 20) + self.assertTrue(mr1.an_attribute) + mr2 = mr1.move(1, 2) + self.assertTrue(isinstance(mr2, self.MyRect)) + self.assertRaises(AttributeError, getattr, mr2, "an_attribute") + + def test_inflate(self): + mr1 = self.MyRect(1, 2, 10, 20) + self.assertTrue(mr1.an_attribute) + mr2 = mr1.inflate(2, 4) + self.assertTrue(isinstance(mr2, self.MyRect)) + self.assertRaises(AttributeError, getattr, mr2, "an_attribute") + + def test_clamp(self): + mr1 = self.MyRect(19, 12, 5, 5) + self.assertTrue(mr1.an_attribute) + mr2 = mr1.clamp(Rect(10, 10, 10, 10)) + self.assertTrue(isinstance(mr2, self.MyRect)) + self.assertRaises(AttributeError, getattr, mr2, "an_attribute") + + def test_clip(self): + mr1 = self.MyRect(1, 2, 3, 4) + self.assertTrue(mr1.an_attribute) + mr2 = mr1.clip(Rect(0, 0, 3, 4)) + self.assertTrue(isinstance(mr2, self.MyRect)) + self.assertRaises(AttributeError, getattr, mr2, "an_attribute") + + def test_union(self): + mr1 = self.MyRect(1, 1, 1, 2) + self.assertTrue(mr1.an_attribute) + mr2 = mr1.union(Rect(-2, -2, 1, 2)) + self.assertTrue(isinstance(mr2, self.MyRect)) + self.assertRaises(AttributeError, getattr, mr2, "an_attribute") + + def test_unionall(self): + mr1 = self.MyRect(0, 0, 1, 1) + self.assertTrue(mr1.an_attribute) + mr2 = mr1.unionall([Rect(-2, -2, 1, 1), Rect(2, 2, 1, 1)]) + self.assertTrue(isinstance(mr2, self.MyRect)) + self.assertRaises(AttributeError, getattr, mr2, "an_attribute") + + def test_fit(self): + mr1 = self.MyRect(10, 10, 30, 30) + self.assertTrue(mr1.an_attribute) + mr2 = mr1.fit(Rect(30, 30, 15, 10)) + self.assertTrue(isinstance(mr2, self.MyRect)) + self.assertRaises(AttributeError, getattr, mr2, "an_attribute") + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/__init__.py new file mode 100644 index 0000000..1bb8bf6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/__init__.py new file mode 100644 index 0000000..1bb8bf6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/fake_2_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/fake_2_test.py new file mode 100644 index 0000000..3be92e1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/fake_3_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/fake_3_test.py new file mode 100644 index 0000000..3be92e1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/fake_3_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/fake_4_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/fake_4_test.py new file mode 100644 index 0000000..3be92e1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/fake_4_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/fake_5_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/fake_5_test.py new file mode 100644 index 0000000..3be92e1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/fake_5_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/fake_6_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/fake_6_test.py new file mode 100644 index 0000000..3be92e1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/fake_6_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/no_assertions__ret_code_of_1__test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/no_assertions__ret_code_of_1__test.py new file mode 100644 index 0000000..0ba0e94 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/no_assertions__ret_code_of_1__test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + pass + + def test_get_mods(self): + pass + + def test_get_pressed(self): + pass + + def test_name(self): + pass + + def test_set_mods(self): + pass + + def test_set_repeat(self): + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/zero_tests_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/zero_tests_test.py new file mode 100644 index 0000000..649055a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/all_ok/zero_tests_test.py @@ -0,0 +1,23 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/everything/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/everything/__init__.py new file mode 100644 index 0000000..1bb8bf6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/everything/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/everything/fake_2_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/everything/fake_2_test.py new file mode 100644 index 0000000..3be92e1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/everything/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/everything/incomplete_todo_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/everything/incomplete_todo_test.py new file mode 100644 index 0000000..bdd8a3b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/everything/incomplete_todo_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def todo_test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def todo_test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/everything/magic_tag_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/everything/magic_tag_test.py new file mode 100644 index 0000000..126bc2b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/everything/magic_tag_test.py @@ -0,0 +1,38 @@ +__tags__ = ["magic"] + +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/everything/sleep_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/everything/sleep_test.py new file mode 100644 index 0000000..468c75f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/everything/sleep_test.py @@ -0,0 +1,29 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + +import time + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + stop_time = time.time() + 10.0 + while time.time() < stop_time: + time.sleep(1) + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/exclude/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/exclude/__init__.py new file mode 100644 index 0000000..1bb8bf6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/exclude/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/exclude/fake_2_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/exclude/fake_2_test.py new file mode 100644 index 0000000..3be92e1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/exclude/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/exclude/invisible_tag_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/exclude/invisible_tag_test.py new file mode 100644 index 0000000..3ef959a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/exclude/invisible_tag_test.py @@ -0,0 +1,41 @@ +__tags__ = ["invisible"] + +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/exclude/magic_tag_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/exclude/magic_tag_test.py new file mode 100644 index 0000000..126bc2b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/exclude/magic_tag_test.py @@ -0,0 +1,38 @@ +__tags__ = ["magic"] + +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/failures1/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/failures1/__init__.py new file mode 100644 index 0000000..1bb8bf6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/failures1/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/failures1/fake_2_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/failures1/fake_2_test.py new file mode 100644 index 0000000..3be92e1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/failures1/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/failures1/fake_3_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/failures1/fake_3_test.py new file mode 100644 index 0000000..3be92e1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/failures1/fake_3_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/failures1/fake_4_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/failures1/fake_4_test.py new file mode 100644 index 0000000..1e75fea --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/failures1/fake_4_test.py @@ -0,0 +1,41 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(False, "Some Jibberish") + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + if 1: + if 1: + assert False + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete/__init__.py new file mode 100644 index 0000000..1bb8bf6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete/fake_2_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete/fake_2_test.py new file mode 100644 index 0000000..b88f1ae --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def todo_test_get_pressed(self): + self.fail() + + def test_name(self): + self.assertTrue(True) + + def todo_test_set_mods(self): + self.fail() + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete/fake_3_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete/fake_3_test.py new file mode 100644 index 0000000..3be92e1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete/fake_3_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete_todo/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete_todo/__init__.py new file mode 100644 index 0000000..1bb8bf6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete_todo/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_2_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_2_test.py new file mode 100644 index 0000000..bdd8a3b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def todo_test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def todo_test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_3_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_3_test.py new file mode 100644 index 0000000..3be92e1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_3_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/infinite_loop/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/infinite_loop/__init__.py new file mode 100644 index 0000000..1bb8bf6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/infinite_loop/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_1_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_1_test.py new file mode 100644 index 0000000..3e9e936 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_1_test.py @@ -0,0 +1,40 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + while True: + pass + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_2_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_2_test.py new file mode 100644 index 0000000..3be92e1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stderr/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stderr/__init__.py new file mode 100644 index 0000000..1bb8bf6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stderr/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_2_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_2_test.py new file mode 100644 index 0000000..3be92e1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_3_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_3_test.py new file mode 100644 index 0000000..f59ad40 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_3_test.py @@ -0,0 +1,41 @@ +import sys + +if __name__ == "__main__": + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + sys.stderr.write("jibberish messes things up\n") + self.assertTrue(False) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_4_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_4_test.py new file mode 100644 index 0000000..1e75fea --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_4_test.py @@ -0,0 +1,41 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(False, "Some Jibberish") + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + if 1: + if 1: + assert False + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stdout/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stdout/__init__.py new file mode 100644 index 0000000..1bb8bf6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stdout/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_2_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_2_test.py new file mode 100644 index 0000000..3be92e1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_3_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_3_test.py new file mode 100644 index 0000000..467c725 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_3_test.py @@ -0,0 +1,42 @@ +import sys + +if __name__ == "__main__": + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + sys.stdout.write("jibberish ruins everything\n") + self.assertTrue(False) + + def test_name(self): + sys.stdout.write("forgot to remove debug crap\n") + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_4_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_4_test.py new file mode 100644 index 0000000..1e75fea --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_4_test.py @@ -0,0 +1,41 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(False, "Some Jibberish") + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + if 1: + if 1: + assert False + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/run_tests__test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/run_tests__test.py new file mode 100644 index 0000000..533f7a0 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/run_tests__test.py @@ -0,0 +1,145 @@ +################################################################################ + +import subprocess, os, sys, re, difflib + +################################################################################ + +IGNORE = (".svn", "infinite_loop") +NORMALIZERS = ( + (r"Ran (\d+) tests in (\d+\.\d+)s", "Ran \\1 tests in X.XXXs"), + (r'File ".*?([^/\\.]+\.py)"', 'File "\\1"'), +) + +################################################################################ + + +def norm_result(result): + "normalize differences, such as timing between output" + for normalizer, replacement in NORMALIZERS: + if hasattr(normalizer, "__call__"): + result = normalizer(result) + else: + result = re.sub(normalizer, replacement, result) + + return result + + +def call_proc(cmd, cd=None): + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + cwd=cd, + universal_newlines=True, + ) + if proc.wait(): + print("%s %s" % (cmd, proc.wait())) + raise Exception(proc.stdout.read()) + + return proc.stdout.read() + + +################################################################################ + +unnormed_diff = "-u" in sys.argv +verbose = "-v" in sys.argv or unnormed_diff +if "-h" in sys.argv or "--help" in sys.argv: + sys.exit( + "\nCOMPARES OUTPUT OF SINGLE VS SUBPROCESS MODE OF RUN_TESTS.PY\n\n" + "-v, to output diffs even on success\n" + "-u, to output diffs of unnormalized tests\n\n" + "Each line of a Differ delta begins with a two-letter code:\n\n" + " '- ' line unique to sequence 1\n" + " '+ ' line unique to sequence 2\n" + " ' ' line common to both sequences\n" + " '? ' line not present in either input sequence\n" + ) + +main_dir = os.path.split(os.path.abspath(sys.argv[0]))[0] +trunk_dir = os.path.normpath(os.path.join(main_dir, "../../")) + +test_suite_dirs = [ + x + for x in os.listdir(main_dir) + if os.path.isdir(os.path.join(main_dir, x)) and x not in IGNORE +] + + +################################################################################ + + +def assert_on_results(suite, single, sub): + test = globals().get("%s_test" % suite) + if hasattr(test, "__call_"): + test(suite, single, sub) + print("assertions on %s OK" % (suite,)) + + +# Don't modify tests in suites below. These assertions are in place to make sure +# that tests are actually being ran + + +def all_ok_test(uite, *args): + for results in args: + assert "Ran 36 tests" in results # some tests are runing + assert "OK" in results # OK + + +def failures1_test(suite, *args): + for results in args: + assert "FAILED (failures=2)" in results + assert "Ran 18 tests" in results + + +################################################################################ +# Test that output is the same in single process and subprocess modes +# + +base_cmd = [sys.executable, "run_tests.py", "-i"] + +cmd = base_cmd + ["-n", "-f"] +sub_cmd = base_cmd + ["-f"] +time_out_cmd = base_cmd + ["-t", "4", "-f", "infinite_loop"] + +passes = 0 +failed = False + +for suite in test_suite_dirs: + single = call_proc(cmd + [suite], trunk_dir) + subs = call_proc(sub_cmd + [suite], trunk_dir) + + normed_single, normed_subs = map(norm_result, (single, subs)) + + failed = normed_single != normed_subs + if failed: + print("%s suite comparison FAILED\n" % (suite,)) + else: + passes += 1 + print("%s suite comparison OK" % (suite,)) + + assert_on_results(suite, single, subs) + + if verbose or failed: + print("difflib.Differ().compare(single, suprocessed):\n") + print( + "".join( + list( + difflib.Differ().compare( + (unnormed_diff and single or normed_single).splitlines(1), + (unnormed_diff and subs or normed_subs).splitlines(1), + ) + ) + ) + ) + +sys.stdout.write("infinite_loop suite (subprocess mode timeout) ") +loop_test = call_proc(time_out_cmd, trunk_dir) +assert "successfully terminated" in loop_test +passes += 1 +print("OK") + +print("\n%s/%s suites pass" % (passes, len(test_suite_dirs) + 1)) + +print("\n-h for help") + +################################################################################ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/timeout/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/timeout/__init__.py new file mode 100644 index 0000000..1bb8bf6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/timeout/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/timeout/fake_2_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/timeout/fake_2_test.py new file mode 100644 index 0000000..3be92e1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/timeout/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/timeout/sleep_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/timeout/sleep_test.py new file mode 100644 index 0000000..bab528a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/run_tests__tests/timeout/sleep_test.py @@ -0,0 +1,30 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + +import time + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + stop_time = time.time() + 10.0 + while time.time() < stop_time: + time.sleep(1) + + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/rwobject_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/rwobject_test.py new file mode 100644 index 0000000..6e420bc --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/rwobject_test.py @@ -0,0 +1,132 @@ +import sys +import unittest + +from pygame import encode_string, encode_file_path +from pygame.compat import bytes_, as_bytes, as_unicode + + +class RWopsEncodeStringTest(unittest.TestCase): + global getrefcount + + def test_obj_None(self): + encoded_string = encode_string(None) + + self.assertIsNone(encoded_string) + + def test_returns_bytes(self): + u = as_unicode(r"Hello") + encoded_string = encode_string(u) + + self.assertIsInstance(encoded_string, bytes_) + + def test_obj_bytes(self): + b = as_bytes("encyclop\xE6dia") + encoded_string = encode_string(b, "ascii", "strict") + + self.assertIs(encoded_string, b) + + def test_encode_unicode(self): + u = as_unicode(r"\u00DEe Olde Komp\u00FCter Shoppe") + b = u.encode("utf-8") + self.assertEqual(encode_string(u, "utf-8"), b) + + def test_error_fowarding(self): + self.assertRaises(SyntaxError, encode_string) + + def test_errors(self): + s = r"abc\u0109defg\u011Dh\u0125ij\u0135klmnoprs\u015Dtu\u016Dvz" + u = as_unicode(s) + b = u.encode("ascii", "ignore") + self.assertEqual(encode_string(u, "ascii", "ignore"), b) + + def test_encoding_error(self): + u = as_unicode(r"a\x80b") + encoded_string = encode_string(u, "ascii", "strict") + + self.assertIsNone(encoded_string) + + def test_check_defaults(self): + u = as_unicode(r"a\u01F7b") + b = u.encode("unicode_escape", "backslashreplace") + encoded_string = encode_string(u) + + self.assertEqual(encoded_string, b) + + def test_etype(self): + u = as_unicode(r"a\x80b") + self.assertRaises(SyntaxError, encode_string, u, "ascii", "strict", SyntaxError) + + def test_etype__invalid(self): + """Ensures invalid etypes are properly handled.""" + + for etype in ("SyntaxError", self): + self.assertRaises(TypeError, encode_string, "test", etype=etype) + + def test_string_with_null_bytes(self): + b = as_bytes("a\x00b\x00c") + encoded_string = encode_string(b, etype=SyntaxError) + encoded_decode_string = encode_string(b.decode(), "ascii", "strict") + + self.assertIs(encoded_string, b) + self.assertEqual(encoded_decode_string, b) + + try: + from sys import getrefcount as _g + + getrefcount = _g # This nonsense is for Python 3.x + except ImportError: + pass + else: + + def test_refcount(self): + bpath = as_bytes(" This is a string that is not cached.")[1:] + upath = bpath.decode("ascii") + before = getrefcount(bpath) + bpath = encode_string(bpath) + self.assertEqual(getrefcount(bpath), before) + bpath = encode_string(upath) + self.assertEqual(getrefcount(bpath), before) + + def test_smp(self): + utf_8 = as_bytes("a\xF0\x93\x82\xA7b") + u = as_unicode(r"a\U000130A7b") + b = encode_string(u, "utf-8", "strict", AssertionError) + self.assertEqual(b, utf_8) + # For Python 3.1, surrogate pair handling depends on whether the + # interpreter was built with UCS-2 or USC-4 unicode strings. + ##u = as_unicode(r"a\uD80C\uDCA7b") + ##b = encode_string(u, 'utf-8', 'strict', AssertionError) + ##self.assertEqual(b, utf_8) + + +class RWopsEncodeFilePathTest(unittest.TestCase): + # Most tests can be skipped since RWopsEncodeFilePath wraps + # RWopsEncodeString + def test_encoding(self): + u = as_unicode(r"Hello") + encoded_file_path = encode_file_path(u) + + self.assertIsInstance(encoded_file_path, bytes_) + + def test_error_fowarding(self): + self.assertRaises(SyntaxError, encode_file_path) + + def test_path_with_null_bytes(self): + b = as_bytes("a\x00b\x00c") + encoded_file_path = encode_file_path(b) + + self.assertIsNone(encoded_file_path) + + def test_etype(self): + b = as_bytes("a\x00b\x00c") + self.assertRaises(TypeError, encode_file_path, b, TypeError) + + def test_etype__invalid(self): + """Ensures invalid etypes are properly handled.""" + + for etype in ("SyntaxError", self): + self.assertRaises(TypeError, encode_file_path, "test", etype) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/scrap_tags.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/scrap_tags.py new file mode 100644 index 0000000..24c415d --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/scrap_tags.py @@ -0,0 +1,21 @@ +# For now the scrap module has not been updated for SDL 2 +__tags__ = ["SDL2_ignore"] + +import sys + +exclude = False + +if sys.platform == "win32" or sys.platform.startswith("linux"): + try: + import pygame + + pygame.scrap._NOT_IMPLEMENTED_ + except AttributeError: + pass + else: + exclude = True +else: + exclude = True + +if exclude: + __tags__.extend(["ignore", "subprocess_ignore"]) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/scrap_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/scrap_test.py new file mode 100644 index 0000000..e27d22f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/scrap_test.py @@ -0,0 +1,302 @@ +import os +import sys + +if os.environ.get("SDL_VIDEODRIVER") == "dummy": + __tags__ = ("ignore", "subprocess_ignore") +import unittest +from pygame.tests.test_utils import trunk_relative_path + +import pygame +from pygame import scrap +from pygame.compat import as_bytes + + +class ScrapModuleTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + pygame.display.init() + pygame.display.set_mode((1, 1)) + scrap.init() + + @classmethod + def tearDownClass(cls): + # scrap.quit() # Does not exist! + pygame.display.quit() + + def test_init(self): + """Ensures scrap module still initialized after multiple init calls.""" + scrap.init() + scrap.init() + + self.assertTrue(scrap.get_init()) + + def test_init__reinit(self): + """Ensures reinitializing the scrap module doesn't clear its data.""" + data_type = pygame.SCRAP_TEXT + expected_data = as_bytes("test_init__reinit") + scrap.put(data_type, expected_data) + + scrap.init() + + self.assertEqual(scrap.get(data_type), expected_data) + + def test_get_init(self): + """Ensures get_init gets the init state.""" + self.assertTrue(scrap.get_init()) + + def todo_test_contains(self): + """Ensures contains works as expected.""" + self.fail() + + def todo_test_get(self): + """Ensures get works as expected.""" + self.fail() + + def test_get__owned_empty_type(self): + """Ensures get works when there is no data of the requested type + in the clipboard and the clipboard is owned by the pygame application. + """ + # Use a unique data type identifier to ensure there is no preexisting + # data. + DATA_TYPE = "test_get__owned_empty_type" + + if scrap.lost(): + # Try to acquire the clipboard. + scrap.put(pygame.SCRAP_TEXT, b"text to clipboard") + + if scrap.lost(): + self.skipTest("requires the pygame application to own the " "clipboard") + + data = scrap.get(DATA_TYPE) + + self.assertIsNone(data) + + def todo_test_get_types(self): + """Ensures get_types works as expected.""" + self.fail() + + def todo_test_lost(self): + """Ensures lost works as expected.""" + self.fail() + + def test_set_mode(self): + """Ensures set_mode works as expected.""" + scrap.set_mode(pygame.SCRAP_SELECTION) + scrap.set_mode(pygame.SCRAP_CLIPBOARD) + + self.assertRaises(ValueError, scrap.set_mode, 1099) + + def test_put__text(self): + """Ensures put can place text into the clipboard.""" + scrap.put(pygame.SCRAP_TEXT, as_bytes("Hello world")) + + self.assertEqual(scrap.get(pygame.SCRAP_TEXT), as_bytes("Hello world")) + + scrap.put(pygame.SCRAP_TEXT, as_bytes("Another String")) + + self.assertEqual(scrap.get(pygame.SCRAP_TEXT), as_bytes("Another String")) + + @unittest.skipIf("pygame.image" not in sys.modules, "requires pygame.image module") + def test_put__bmp_image(self): + """Ensures put can place a BMP image into the clipboard.""" + sf = pygame.image.load(trunk_relative_path("examples/data/asprite.bmp")) + expected_string = pygame.image.tostring(sf, "RGBA") + scrap.put(pygame.SCRAP_BMP, expected_string) + + self.assertEqual(scrap.get(pygame.SCRAP_BMP), expected_string) + + def test_put(self): + """Ensures put can place data into the clipboard + when using a user defined type identifier. + """ + DATA_TYPE = "arbitrary buffer" + + scrap.put(DATA_TYPE, as_bytes("buf")) + r = scrap.get(DATA_TYPE) + + self.assertEqual(r, as_bytes("buf")) + + +class ScrapModuleClipboardNotOwnedTest(unittest.TestCase): + """Test the scrap module's functionality when the pygame application is + not the current owner of the clipboard. + + A separate class is used to prevent tests that acquire the clipboard from + interfering with these tests. + """ + + @classmethod + def setUpClass(cls): + pygame.display.init() + pygame.display.set_mode((1, 1)) + scrap.init() + + @classmethod + def tearDownClass(cls): + # scrap.quit() # Does not exist! + pygame.quit() + pygame.display.quit() + + def _skip_if_clipboard_owned(self): + # Skip test if the pygame application owns the clipboard. Currently, + # there is no way to give up ownership. + if not scrap.lost(): + self.skipTest("requires the pygame application to not own the " "clipboard") + + def test_get__not_owned(self): + """Ensures get works when there is no data of the requested type + in the clipboard and the clipboard is not owned by the pygame + application. + """ + self._skip_if_clipboard_owned() + + # Use a unique data type identifier to ensure there is no preexisting + # data. + DATA_TYPE = "test_get__not_owned" + + data = scrap.get(DATA_TYPE) + + self.assertIsNone(data) + + def test_get_types__not_owned(self): + """Ensures get_types works when the clipboard is not owned + by the pygame application. + """ + self._skip_if_clipboard_owned() + + data_types = scrap.get_types() + + self.assertIsInstance(data_types, list) + + def test_contains__not_owned(self): + """Ensures contains works when the clipboard is not owned + by the pygame application. + """ + self._skip_if_clipboard_owned() + + # Use a unique data type identifier to ensure there is no preexisting + # data. + DATA_TYPE = "test_contains__not_owned" + + contains = scrap.contains(DATA_TYPE) + + self.assertFalse(contains) + + def test_lost__not_owned(self): + """Ensures lost works when the clipboard is not owned + by the pygame application. + """ + self._skip_if_clipboard_owned() + + lost = scrap.lost() + + self.assertTrue(lost) + + +class X11InteractiveTest(unittest.TestCase): + __tags__ = ["ignore", "subprocess_ignore"] + try: + pygame.display.init() + except Exception: + pass + else: + if pygame.display.get_driver() == "x11": + __tags__ = ["interactive"] + pygame.display.quit() + + def test_issue_208(self): + """PATCH: pygame.scrap on X11, fix copying into PRIMARY selection + + Copying into theX11 PRIMARY selection (mouse copy/paste) would not + work due to a confusion between content type and clipboard type. + + """ + + from pygame import display, event, freetype + from pygame.locals import SCRAP_SELECTION, SCRAP_TEXT + from pygame.locals import KEYDOWN, K_y, QUIT + + success = False + freetype.init() + font = freetype.Font(None, 24) + display.init() + display.set_caption("Interactive X11 Paste Test") + screen = display.set_mode((600, 200)) + screen.fill(pygame.Color("white")) + text = "Scrap put() succeeded." + msg = ( + "Some text has been placed into the X11 clipboard." + " Please click the center mouse button in an open" + " text window to retrieve it." + '\n\nDid you get "{}"? (y/n)' + ).format(text) + word_wrap(screen, msg, font, 6) + display.flip() + event.pump() + scrap.init() + scrap.set_mode(SCRAP_SELECTION) + scrap.put(SCRAP_TEXT, text.encode("UTF-8")) + while True: + e = event.wait() + if e.type == QUIT: + break + if e.type == KEYDOWN: + success = e.key == K_y + break + pygame.display.quit() + self.assertTrue(success) + + +def word_wrap(surf, text, font, margin=0, color=(0, 0, 0)): + font.origin = True + surf_width, surf_height = surf.get_size() + width = surf_width - 2 * margin + height = surf_height - 2 * margin + line_spacing = int(1.25 * font.get_sized_height()) + x, y = margin, margin + line_spacing + space = font.get_rect(" ") + for word in iwords(text): + if word == "\n": + x, y = margin, y + line_spacing + else: + bounds = font.get_rect(word) + if x + bounds.width + bounds.x >= width: + x, y = margin, y + line_spacing + if x + bounds.width + bounds.x >= width: + raise ValueError("word too wide for the surface") + if y + bounds.height - bounds.y >= height: + raise ValueError("text to long for the surface") + font.render_to(surf, (x, y), None, color) + x += bounds.width + space.width + return x, y + + +def iwords(text): + # r"\n|[^ ]+" + # + head = 0 + tail = head + end = len(text) + while head < end: + if text[head] == " ": + head += 1 + tail = head + 1 + elif text[head] == "\n": + head += 1 + yield "\n" + tail = head + 1 + elif tail == end: + yield text[head:] + head = end + elif text[tail] == "\n": + yield text[head:tail] + head = tail + elif text[tail] == " ": + yield text[head:tail] + head = tail + else: + tail += 1 + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/sndarray_tags.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/sndarray_tags.py new file mode 100644 index 0000000..68fa7a5 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/sndarray_tags.py @@ -0,0 +1,12 @@ +__tags__ = ["array"] + +exclude = False + +try: + import pygame.mixer + import numpy +except ImportError: + exclude = True + +if exclude: + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/sndarray_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/sndarray_test.py new file mode 100644 index 0000000..5df257f --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/sndarray_test.py @@ -0,0 +1,165 @@ +import unittest + +from numpy import int8, int16, uint8, uint16, float32, array, alltrue + +import pygame +from pygame.compat import as_bytes +import pygame.sndarray + + +SDL2 = pygame.get_sdl_version()[0] >= 2 + + +class SndarrayTest(unittest.TestCase): + array_dtypes = {8: uint8, -8: int8, 16: uint16, -16: int16, 32: float32} + + def _assert_compatible(self, arr, size): + dtype = self.array_dtypes[size] + self.assertEqual(arr.dtype, dtype) + + def test_array(self): + def check_array(size, channels, test_data): + try: + pygame.mixer.init(22050, size, channels, allowedchanges=0) + except pygame.error: + # Not all sizes are supported on all systems. + return + try: + __, sz, __ = pygame.mixer.get_init() + if sz == size: + srcarr = array(test_data, self.array_dtypes[size]) + snd = pygame.sndarray.make_sound(srcarr) + arr = pygame.sndarray.array(snd) + self._assert_compatible(arr, size) + self.assertTrue( + alltrue(arr == srcarr), + "size: %i\n%s\n%s" % (size, arr, test_data), + ) + finally: + pygame.mixer.quit() + + check_array(8, 1, [0, 0x0F, 0xF0, 0xFF]) + check_array(8, 2, [[0, 0x80], [0x2D, 0x41], [0x64, 0xA1], [0xFF, 0x40]]) + check_array(16, 1, [0, 0x00FF, 0xFF00, 0xFFFF]) + check_array( + 16, 2, [[0, 0xFFFF], [0xFFFF, 0], [0x00FF, 0xFF00], [0x0F0F, 0xF0F0]] + ) + check_array(-8, 1, [0, -0x80, 0x7F, 0x64]) + check_array(-8, 2, [[0, -0x80], [-0x64, 0x64], [0x25, -0x50], [0xFF, 0]]) + check_array(-16, 1, [0, 0x7FFF, -0x7FFF, -1]) + check_array(-16, 2, [[0, -0x7FFF], [-0x7FFF, 0], [0x7FFF, 0], [0, 0x7FFF]]) + + def test_get_arraytype(self): + array_type = pygame.sndarray.get_arraytype() + + self.assertEqual(array_type, "numpy", "unknown array type %s" % array_type) + + def test_get_arraytypes(self): + arraytypes = pygame.sndarray.get_arraytypes() + self.assertIn("numpy", arraytypes) + + for atype in arraytypes: + self.assertEqual(atype, "numpy", "unknown array type %s" % atype) + + def test_make_sound(self): + def check_sound(size, channels, test_data): + try: + pygame.mixer.init(22050, size, channels, allowedchanges=0) + except pygame.error: + # Not all sizes are supported on all systems. + return + try: + __, sz, __ = pygame.mixer.get_init() + if sz == size: + srcarr = array(test_data, self.array_dtypes[size]) + snd = pygame.sndarray.make_sound(srcarr) + arr = pygame.sndarray.samples(snd) + self.assertTrue( + alltrue(arr == srcarr), + "size: %i\n%s\n%s" % (size, arr, test_data), + ) + finally: + pygame.mixer.quit() + + check_sound(8, 1, [0, 0x0F, 0xF0, 0xFF]) + check_sound(8, 2, [[0, 0x80], [0x2D, 0x41], [0x64, 0xA1], [0xFF, 0x40]]) + check_sound(16, 1, [0, 0x00FF, 0xFF00, 0xFFFF]) + check_sound( + 16, 2, [[0, 0xFFFF], [0xFFFF, 0], [0x00FF, 0xFF00], [0x0F0F, 0xF0F0]] + ) + check_sound(-8, 1, [0, -0x80, 0x7F, 0x64]) + check_sound(-8, 2, [[0, -0x80], [-0x64, 0x64], [0x25, -0x50], [0xFF, 0]]) + check_sound(-16, 1, [0, 0x7FFF, -0x7FFF, -1]) + check_sound(-16, 2, [[0, -0x7FFF], [-0x7FFF, 0], [0x7FFF, 0], [0, 0x7FFF]]) + + if SDL2: + check_sound(32, 2, [[0.0, -1.0], [-1.0, 0], [1.0, 0], [0, 1.0]]) + + def test_samples(self): + + null_byte = as_bytes("\x00") + + def check_sample(size, channels, test_data): + try: + pygame.mixer.init(22050, size, channels, allowedchanges=0) + except pygame.error: + # Not all sizes are supported on all systems. + return + try: + __, sz, __ = pygame.mixer.get_init() + if sz == size: + zeroed = null_byte * ((abs(size) // 8) * len(test_data) * channels) + snd = pygame.mixer.Sound(buffer=zeroed) + samples = pygame.sndarray.samples(snd) + self._assert_compatible(samples, size) + ##print ('X %s' % (samples.shape,)) + ##print ('Y %s' % (test_data,)) + samples[...] = test_data + arr = pygame.sndarray.array(snd) + self.assertTrue( + alltrue(samples == arr), + "size: %i\n%s\n%s" % (size, arr, test_data), + ) + finally: + pygame.mixer.quit() + + check_sample(8, 1, [0, 0x0F, 0xF0, 0xFF]) + check_sample(8, 2, [[0, 0x80], [0x2D, 0x41], [0x64, 0xA1], [0xFF, 0x40]]) + check_sample(16, 1, [0, 0x00FF, 0xFF00, 0xFFFF]) + check_sample( + 16, 2, [[0, 0xFFFF], [0xFFFF, 0], [0x00FF, 0xFF00], [0x0F0F, 0xF0F0]] + ) + check_sample(-8, 1, [0, -0x80, 0x7F, 0x64]) + check_sample(-8, 2, [[0, -0x80], [-0x64, 0x64], [0x25, -0x50], [0xFF, 0]]) + check_sample(-16, 1, [0, 0x7FFF, -0x7FFF, -1]) + check_sample(-16, 2, [[0, -0x7FFF], [-0x7FFF, 0], [0x7FFF, 0], [0, 0x7FFF]]) + + if SDL2: + check_sample(32, 2, [[0.0, -1.0], [-1.0, 0], [1.0, 0], [0, 1.0]]) + + def test_use_arraytype(self): + def do_use_arraytype(atype): + pygame.sndarray.use_arraytype(atype) + + pygame.sndarray.use_arraytype("numpy") + self.assertEqual(pygame.sndarray.get_arraytype(), "numpy") + + self.assertRaises(ValueError, do_use_arraytype, "not an option") + + @unittest.skipIf(not SDL2, "requires SDL2") + def test_float32(self): + """ sized arrays work with Sounds and 32bit float arrays. + """ + try: + pygame.mixer.init(22050, 32, 2, allowedchanges=0) + except pygame.error: + # Not all sizes are supported on all systems. + self.skipTest("unsupported mixer configuration") + + arr = array([[0.0, -1.0], [-1.0, 0], [1.0, 0], [0, 1.0]], float32) + newsound = pygame.mixer.Sound(array=arr) + pygame.mixer.quit() + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/sprite_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/sprite_test.py new file mode 100644 index 0000000..4b8097c --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/sprite_test.py @@ -0,0 +1,1361 @@ +#################################### IMPORTS ################################### +# -*- encoding: utf-8 -*- + + +import unittest + +import pygame +from pygame import sprite + + +################################# MODULE LEVEL ################################# + + +class SpriteModuleTest(unittest.TestCase): + pass + + +######################### SPRITECOLLIDE FUNCTIONS TEST ######################### + + +class SpriteCollideTest(unittest.TestCase): + def setUp(self): + self.ag = sprite.AbstractGroup() + self.ag2 = sprite.AbstractGroup() + self.s1 = sprite.Sprite(self.ag) + self.s2 = sprite.Sprite(self.ag2) + self.s3 = sprite.Sprite(self.ag2) + + self.s1.image = pygame.Surface((50, 10), pygame.SRCALPHA, 32) + self.s2.image = pygame.Surface((10, 10), pygame.SRCALPHA, 32) + self.s3.image = pygame.Surface((10, 10), pygame.SRCALPHA, 32) + + self.s1.rect = self.s1.image.get_rect() + self.s2.rect = self.s2.image.get_rect() + self.s3.rect = self.s3.image.get_rect() + self.s2.rect.move_ip(40, 0) + self.s3.rect.move_ip(100, 100) + + def test_spritecollide__works_if_collided_cb_is_None(self): + # Test that sprites collide without collided function. + self.assertEqual( + sprite.spritecollide(self.s1, self.ag2, dokill=False, collided=None), + [self.s2], + ) + + def test_spritecollide__works_if_collided_cb_not_passed(self): + # Should also work when collided function isn't passed at all. + self.assertEqual( + sprite.spritecollide(self.s1, self.ag2, dokill=False), [self.s2] + ) + + def test_spritecollide__collided_must_be_a_callable(self): + # Need to pass a callable. + self.assertRaises( + TypeError, sprite.spritecollide, self.s1, self.ag2, dokill=False, collided=1 + ) + + def test_spritecollide__collided_defaults_to_collide_rect(self): + # collide_rect should behave the same as default. + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_rect + ), + [self.s2], + ) + + def test_collide_rect_ratio__ratio_of_one_like_default(self): + # collide_rect_ratio should behave the same as default at a 1.0 ratio. + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_rect_ratio(1.0) + ), + [self.s2], + ) + + def test_collide_rect_ratio__collides_all_at_ratio_of_twenty(self): + # collide_rect_ratio should collide all at a 20.0 ratio. + collided_func = sprite.collide_rect_ratio(20.0) + expected_sprites = sorted(self.ag2.sprites(), key=id) + + collided_sprites = sorted( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=collided_func + ), + key=id, + ) + + self.assertListEqual(collided_sprites, expected_sprites) + + def test_collide_circle__no_radius_set(self): + # collide_circle with no radius set. + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_circle + ), + [self.s2], + ) + + def test_collide_circle_ratio__no_radius_and_ratio_of_one(self): + # collide_circle_ratio with no radius set, at a 1.0 ratio. + self.assertEqual( + sprite.spritecollide( + self.s1, + self.ag2, + dokill=False, + collided=sprite.collide_circle_ratio(1.0), + ), + [self.s2], + ) + + def test_collide_circle_ratio__no_radius_and_ratio_of_twenty(self): + # collide_circle_ratio with no radius set, at a 20.0 ratio. + collided_func = sprite.collide_circle_ratio(20.0) + expected_sprites = sorted(self.ag2.sprites(), key=id) + + collided_sprites = sorted( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=collided_func + ), + key=id, + ) + + self.assertListEqual(expected_sprites, collided_sprites) + + def test_collide_circle__with_radii_set(self): + # collide_circle with a radius set. + self.s1.radius = 50 + self.s2.radius = 10 + self.s3.radius = 400 + collided_func = sprite.collide_circle + expected_sprites = sorted(self.ag2.sprites(), key=id) + + collided_sprites = sorted( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=collided_func + ), + key=id, + ) + + self.assertListEqual(expected_sprites, collided_sprites) + + def test_collide_circle_ratio__with_radii_set(self): + # collide_circle_ratio with a radius set. + self.s1.radius = 50 + self.s2.radius = 10 + self.s3.radius = 400 + collided_func = sprite.collide_circle_ratio(0.5) + expected_sprites = sorted(self.ag2.sprites(), key=id) + + collided_sprites = sorted( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=collided_func + ), + key=id, + ) + + self.assertListEqual(expected_sprites, collided_sprites) + + def test_collide_mask__opaque(self): + # make some fully opaque sprites that will collide with masks. + self.s1.image.fill((255, 255, 255, 255)) + self.s2.image.fill((255, 255, 255, 255)) + self.s3.image.fill((255, 255, 255, 255)) + + # masks should be autogenerated from image if they don't exist. + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_mask + ), + [self.s2], + ) + + self.s1.mask = pygame.mask.from_surface(self.s1.image) + self.s2.mask = pygame.mask.from_surface(self.s2.image) + self.s3.mask = pygame.mask.from_surface(self.s3.image) + + # with set masks. + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_mask + ), + [self.s2], + ) + + def test_collide_mask__transparent(self): + # make some sprites that are fully transparent, so they won't collide. + self.s1.image.fill((255, 255, 255, 0)) + self.s2.image.fill((255, 255, 255, 0)) + self.s3.image.fill((255, 255, 255, 0)) + + self.s1.mask = pygame.mask.from_surface(self.s1.image, 255) + self.s2.mask = pygame.mask.from_surface(self.s2.image, 255) + self.s3.mask = pygame.mask.from_surface(self.s3.image, 255) + + self.assertFalse( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_mask + ) + ) + + def test_spritecollideany__without_collided_callback(self): + + # pygame.sprite.spritecollideany(sprite, group) -> sprite + # finds any sprites that collide + + # if collided is not passed, all + # sprites must have a "rect" value, which is a + # rectangle of the sprite area, which will be used + # to calculate the collision. + + # s2 in, s3 out + expected_sprite = self.s2 + collided_sprite = sprite.spritecollideany(self.s1, self.ag2) + + self.assertEqual(collided_sprite, expected_sprite) + + # s2 and s3 out + self.s2.rect.move_ip(0, 10) + collided_sprite = sprite.spritecollideany(self.s1, self.ag2) + + self.assertIsNone(collided_sprite) + + # s2 out, s3 in + self.s3.rect.move_ip(-105, -105) + expected_sprite = self.s3 + collided_sprite = sprite.spritecollideany(self.s1, self.ag2) + + self.assertEqual(collided_sprite, expected_sprite) + + # s2 and s3 in + self.s2.rect.move_ip(0, -10) + expected_sprite_choices = self.ag2.sprites() + collided_sprite = sprite.spritecollideany(self.s1, self.ag2) + + self.assertIn(collided_sprite, expected_sprite_choices) + + def test_spritecollideany__with_collided_callback(self): + + # pygame.sprite.spritecollideany(sprite, group) -> sprite + # finds any sprites that collide + + # collided is a callback function used to calculate if + # two sprites are colliding. it should take two sprites + # as values, and return a bool value indicating if + # they are colliding. + + # This collision test can be faster than pygame.sprite.spritecollide() + # since it has less work to do. + + arg_dict_a = {} + arg_dict_b = {} + return_container = [True] + + # This function is configurable using the mutable default arguments! + def collided_callback( + spr_a, + spr_b, + arg_dict_a=arg_dict_a, + arg_dict_b=arg_dict_b, + return_container=return_container, + ): + + count = arg_dict_a.get(spr_a, 0) + arg_dict_a[spr_a] = 1 + count + + count = arg_dict_b.get(spr_b, 0) + arg_dict_b[spr_b] = 1 + count + + return return_container[0] + + # This should return a sprite from self.ag2 because the callback + # function (collided_callback()) currently returns True. + expected_sprite_choices = self.ag2.sprites() + collided_sprite = sprite.spritecollideany(self.s1, self.ag2, collided_callback) + + self.assertIn(collided_sprite, expected_sprite_choices) + + # The callback function should have been called only once, so self.s1 + # should have only been passed as an argument once + self.assertEqual(len(arg_dict_a), 1) + self.assertEqual(arg_dict_a[self.s1], 1) + + # The callback function should have been called only once, so self.s2 + # exclusive-or self.s3 should have only been passed as an argument + # once + self.assertEqual(len(arg_dict_b), 1) + self.assertEqual(list(arg_dict_b.values())[0], 1) + self.assertTrue(self.s2 in arg_dict_b or self.s3 in arg_dict_b) + + arg_dict_a.clear() + arg_dict_b.clear() + return_container[0] = False + + # This should return None because the callback function + # (collided_callback()) currently returns False. + collided_sprite = sprite.spritecollideany(self.s1, self.ag2, collided_callback) + + self.assertIsNone(collided_sprite) + + # The callback function should have been called as many times as + # there are sprites in self.ag2 + self.assertEqual(len(arg_dict_a), 1) + self.assertEqual(arg_dict_a[self.s1], len(self.ag2)) + self.assertEqual(len(arg_dict_b), len(self.ag2)) + + # Each sprite in self.ag2 should be called once. + for s in self.ag2: + self.assertEqual(arg_dict_b[s], 1) + + def test_groupcollide__without_collided_callback(self): + + # pygame.sprite.groupcollide(groupa, groupb, dokilla, dokillb) -> dict + # collision detection between group and group + + # test no kill + expected_dict = {self.s1: [self.s2]} + crashed = pygame.sprite.groupcollide(self.ag, self.ag2, False, False) + + self.assertDictEqual(expected_dict, crashed) + + crashed = pygame.sprite.groupcollide(self.ag, self.ag2, False, False) + + self.assertDictEqual(expected_dict, crashed) + + # Test dokill2=True (kill colliding sprites in second group). + crashed = pygame.sprite.groupcollide(self.ag, self.ag2, False, True) + + self.assertDictEqual(expected_dict, crashed) + + expected_dict = {} + crashed = pygame.sprite.groupcollide(self.ag, self.ag2, False, False) + + self.assertDictEqual(expected_dict, crashed) + + # Test dokill1=True (kill colliding sprites in first group). + self.s3.rect.move_ip(-100, -100) + expected_dict = {self.s1: [self.s3]} + crashed = pygame.sprite.groupcollide(self.ag, self.ag2, True, False) + + self.assertDictEqual(expected_dict, crashed) + + expected_dict = {} + crashed = pygame.sprite.groupcollide(self.ag, self.ag2, False, False) + + self.assertDictEqual(expected_dict, crashed) + + def test_groupcollide__with_collided_callback(self): + + collided_callback_true = lambda spr_a, spr_b: True + collided_callback_false = lambda spr_a, spr_b: False + + # test no kill + expected_dict = {} + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, False, collided_callback_false + ) + + self.assertDictEqual(expected_dict, crashed) + + expected_dict = {self.s1: sorted(self.ag2.sprites(), key=id)} + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, False, collided_callback_true + ) + for value in crashed.values(): + value.sort(key=id) + + self.assertDictEqual(expected_dict, crashed) + + # expected_dict is the same again for this collide + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, False, collided_callback_true + ) + for value in crashed.values(): + value.sort(key=id) + + self.assertDictEqual(expected_dict, crashed) + + # Test dokill2=True (kill colliding sprites in second group). + expected_dict = {} + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, True, collided_callback_false + ) + + self.assertDictEqual(expected_dict, crashed) + + expected_dict = {self.s1: sorted(self.ag2.sprites(), key=id)} + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, True, collided_callback_true + ) + for value in crashed.values(): + value.sort(key=id) + + self.assertDictEqual(expected_dict, crashed) + + expected_dict = {} + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, True, collided_callback_true + ) + + self.assertDictEqual(expected_dict, crashed) + + # Test dokill1=True (kill colliding sprites in first group). + self.ag.add(self.s2) + self.ag2.add(self.s3) + expected_dict = {} + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, True, False, collided_callback_false + ) + + self.assertDictEqual(expected_dict, crashed) + + expected_dict = {self.s1: [self.s3], self.s2: [self.s3]} + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, True, False, collided_callback_true + ) + + self.assertDictEqual(expected_dict, crashed) + + expected_dict = {} + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, True, False, collided_callback_true + ) + + self.assertDictEqual(expected_dict, crashed) + + def test_collide_rect(self): + # Test colliding - some edges touching + self.assertTrue(pygame.sprite.collide_rect(self.s1, self.s2)) + self.assertTrue(pygame.sprite.collide_rect(self.s2, self.s1)) + + # Test colliding - all edges touching + self.s2.rect.center = self.s3.rect.center + + self.assertTrue(pygame.sprite.collide_rect(self.s2, self.s3)) + self.assertTrue(pygame.sprite.collide_rect(self.s3, self.s2)) + + # Test colliding - no edges touching + self.s2.rect.inflate_ip(10, 10) + + self.assertTrue(pygame.sprite.collide_rect(self.s2, self.s3)) + self.assertTrue(pygame.sprite.collide_rect(self.s3, self.s2)) + + # Test colliding - some edges intersecting + self.s2.rect.center = (self.s1.rect.right, self.s1.rect.bottom) + + self.assertTrue(pygame.sprite.collide_rect(self.s1, self.s2)) + self.assertTrue(pygame.sprite.collide_rect(self.s2, self.s1)) + + # Test not colliding + self.assertFalse(pygame.sprite.collide_rect(self.s1, self.s3)) + self.assertFalse(pygame.sprite.collide_rect(self.s3, self.s1)) + + +################################################################################ + + +class AbstractGroupTypeTest(unittest.TestCase): + def setUp(self): + self.ag = sprite.AbstractGroup() + self.ag2 = sprite.AbstractGroup() + self.s1 = sprite.Sprite(self.ag) + self.s2 = sprite.Sprite(self.ag) + self.s3 = sprite.Sprite(self.ag2) + self.s4 = sprite.Sprite(self.ag2) + + self.s1.image = pygame.Surface((10, 10)) + self.s1.image.fill(pygame.Color("red")) + self.s1.rect = self.s1.image.get_rect() + + self.s2.image = pygame.Surface((10, 10)) + self.s2.image.fill(pygame.Color("green")) + self.s2.rect = self.s2.image.get_rect() + self.s2.rect.left = 10 + + self.s3.image = pygame.Surface((10, 10)) + self.s3.image.fill(pygame.Color("blue")) + self.s3.rect = self.s3.image.get_rect() + self.s3.rect.top = 10 + + self.s4.image = pygame.Surface((10, 10)) + self.s4.image.fill(pygame.Color("white")) + self.s4.rect = self.s4.image.get_rect() + self.s4.rect.left = 10 + self.s4.rect.top = 10 + + self.bg = pygame.Surface((20, 20)) + self.scr = pygame.Surface((20, 20)) + self.scr.fill(pygame.Color("grey")) + + def test_has(self): + " See if AbstractGroup.has() works as expected. " + + self.assertEqual(True, self.s1 in self.ag) + + self.assertEqual(True, self.ag.has(self.s1)) + + self.assertEqual(True, self.ag.has([self.s1, self.s2])) + + # see if one of them not being in there. + self.assertNotEqual(True, self.ag.has([self.s1, self.s2, self.s3])) + self.assertNotEqual(True, self.ag.has(self.s1, self.s2, self.s3)) + self.assertNotEqual(True, self.ag.has(self.s1, sprite.Group(self.s2, self.s3))) + self.assertNotEqual(True, self.ag.has(self.s1, [self.s2, self.s3])) + + # test empty list processing + self.assertFalse(self.ag.has(*[])) + self.assertFalse(self.ag.has([])) + self.assertFalse(self.ag.has([[]])) + + # see if a second AbstractGroup works. + self.assertEqual(True, self.ag2.has(self.s3)) + + def test_add(self): + ag3 = sprite.AbstractGroup() + sprites = (self.s1, self.s2, self.s3, self.s4) + + for s in sprites: + self.assertNotIn(s, ag3) + + ag3.add(self.s1, [self.s2], self.ag2) + + for s in sprites: + self.assertIn(s, ag3) + + def test_add_internal(self): + self.assertNotIn(self.s1, self.ag2) + + self.ag2.add_internal(self.s1) + + self.assertIn(self.s1, self.ag2) + + def test_clear(self): + + self.ag.draw(self.scr) + self.ag.clear(self.scr, self.bg) + self.assertEqual((0, 0, 0, 255), self.scr.get_at((5, 5))) + self.assertEqual((0, 0, 0, 255), self.scr.get_at((15, 5))) + + def test_draw(self): + + self.ag.draw(self.scr) + self.assertEqual((255, 0, 0, 255), self.scr.get_at((5, 5))) + self.assertEqual((0, 255, 0, 255), self.scr.get_at((15, 5))) + + def test_empty(self): + + self.ag.empty() + self.assertFalse(self.s1 in self.ag) + self.assertFalse(self.s2 in self.ag) + + def test_has_internal(self): + self.assertTrue(self.ag.has_internal(self.s1)) + self.assertFalse(self.ag.has_internal(self.s3)) + + def test_remove(self): + + # Test removal of 1 sprite + self.ag.remove(self.s1) + self.assertFalse(self.ag in self.s1.groups()) + self.assertFalse(self.ag.has(self.s1)) + + # Test removal of 2 sprites as 2 arguments + self.ag2.remove(self.s3, self.s4) + self.assertFalse(self.ag2 in self.s3.groups()) + self.assertFalse(self.ag2 in self.s4.groups()) + self.assertFalse(self.ag2.has(self.s3, self.s4)) + + # Test removal of 4 sprites as a list containing a sprite and a group + # containing a sprite and another group containing 2 sprites. + self.ag.add(self.s1, self.s3, self.s4) + self.ag2.add(self.s3, self.s4) + g = sprite.Group(self.s2) + self.ag.remove([self.s1, g], self.ag2) + self.assertFalse(self.ag in self.s1.groups()) + self.assertFalse(self.ag in self.s2.groups()) + self.assertFalse(self.ag in self.s3.groups()) + self.assertFalse(self.ag in self.s4.groups()) + self.assertFalse(self.ag.has(self.s1, self.s2, self.s3, self.s4)) + + def test_remove_internal(self): + + self.ag.remove_internal(self.s1) + self.assertFalse(self.ag.has_internal(self.s1)) + + def test_sprites(self): + expected_sprites = sorted((self.s1, self.s2), key=id) + sprite_list = sorted(self.ag.sprites(), key=id) + + self.assertListEqual(sprite_list, expected_sprites) + + def test_update(self): + class test_sprite(pygame.sprite.Sprite): + sink = [] + + def __init__(self, *groups): + pygame.sprite.Sprite.__init__(self, *groups) + + def update(self, *args): + self.sink += args + + s = test_sprite(self.ag) + self.ag.update(1, 2, 3) + + self.assertEqual(test_sprite.sink, [1, 2, 3]) + + def test_update_with_kwargs(self): + class test_sprite(pygame.sprite.Sprite): + sink = [] + sink_kwargs = {} + + def __init__(self, *groups): + pygame.sprite.Sprite.__init__(self, *groups) + + def update(self, *args, **kwargs): + self.sink += args + self.sink_kwargs.update(kwargs) + + s = test_sprite(self.ag) + self.ag.update(1, 2, 3, foo=4, bar=5) + + self.assertEqual(test_sprite.sink, [1, 2, 3]) + self.assertEqual(test_sprite.sink_kwargs, {"foo": 4, "bar": 5}) + + +################################################################################ + +# A base class to share tests between similar classes + + +class LayeredGroupBase: + def test_get_layer_of_sprite(self): + expected_layer = 666 + spr = self.sprite() + self.LG.add(spr, layer=expected_layer) + layer = self.LG.get_layer_of_sprite(spr) + + self.assertEqual(len(self.LG._spritelist), 1) + self.assertEqual(layer, self.LG.get_layer_of_sprite(spr)) + self.assertEqual(layer, expected_layer) + self.assertEqual(layer, self.LG._spritelayers[spr]) + + def test_add(self): + expected_layer = self.LG._default_layer + spr = self.sprite() + self.LG.add(spr) + layer = self.LG.get_layer_of_sprite(spr) + + self.assertEqual(len(self.LG._spritelist), 1) + self.assertEqual(layer, expected_layer) + + def test_add__sprite_with_layer_attribute(self): + expected_layer = 100 + spr = self.sprite() + spr._layer = expected_layer + self.LG.add(spr) + layer = self.LG.get_layer_of_sprite(spr) + + self.assertEqual(len(self.LG._spritelist), 1) + self.assertEqual(layer, expected_layer) + + def test_add__passing_layer_keyword(self): + expected_layer = 100 + spr = self.sprite() + self.LG.add(spr, layer=expected_layer) + layer = self.LG.get_layer_of_sprite(spr) + + self.assertEqual(len(self.LG._spritelist), 1) + self.assertEqual(layer, expected_layer) + + def test_add__overriding_sprite_layer_attr(self): + expected_layer = 200 + spr = self.sprite() + spr._layer = 100 + self.LG.add(spr, layer=expected_layer) + layer = self.LG.get_layer_of_sprite(spr) + + self.assertEqual(len(self.LG._spritelist), 1) + self.assertEqual(layer, expected_layer) + + def test_add__adding_sprite_on_init(self): + spr = self.sprite() + lrg2 = sprite.LayeredUpdates(spr) + expected_layer = lrg2._default_layer + layer = lrg2._spritelayers[spr] + + self.assertEqual(len(lrg2._spritelist), 1) + self.assertEqual(layer, expected_layer) + + def test_add__sprite_init_layer_attr(self): + expected_layer = 20 + spr = self.sprite() + spr._layer = expected_layer + lrg2 = sprite.LayeredUpdates(spr) + layer = lrg2._spritelayers[spr] + + self.assertEqual(len(lrg2._spritelist), 1) + self.assertEqual(layer, expected_layer) + + def test_add__sprite_init_passing_layer(self): + expected_layer = 33 + spr = self.sprite() + lrg2 = sprite.LayeredUpdates(spr, layer=expected_layer) + layer = lrg2._spritelayers[spr] + + self.assertEqual(len(lrg2._spritelist), 1) + self.assertEqual(layer, expected_layer) + + def test_add__sprite_init_overiding_layer(self): + expected_layer = 33 + spr = self.sprite() + spr._layer = 55 + lrg2 = sprite.LayeredUpdates(spr, layer=expected_layer) + layer = lrg2._spritelayers[spr] + + self.assertEqual(len(lrg2._spritelist), 1) + self.assertEqual(layer, expected_layer) + + def test_add__spritelist(self): + expected_layer = self.LG._default_layer + sprite_count = 10 + sprites = [self.sprite() for _ in range(sprite_count)] + + self.LG.add(sprites) + + self.assertEqual(len(self.LG._spritelist), sprite_count) + + for i in range(sprite_count): + layer = self.LG.get_layer_of_sprite(sprites[i]) + + self.assertEqual(layer, expected_layer) + + def test_add__spritelist_with_layer_attr(self): + sprites = [] + sprite_and_layer_count = 10 + for i in range(sprite_and_layer_count): + sprites.append(self.sprite()) + sprites[-1]._layer = i + + self.LG.add(sprites) + + self.assertEqual(len(self.LG._spritelist), sprite_and_layer_count) + + for i in range(sprite_and_layer_count): + layer = self.LG.get_layer_of_sprite(sprites[i]) + + self.assertEqual(layer, i) + + def test_add__spritelist_passing_layer(self): + expected_layer = 33 + sprite_count = 10 + sprites = [self.sprite() for _ in range(sprite_count)] + + self.LG.add(sprites, layer=expected_layer) + + self.assertEqual(len(self.LG._spritelist), sprite_count) + + for i in range(sprite_count): + layer = self.LG.get_layer_of_sprite(sprites[i]) + + self.assertEqual(layer, expected_layer) + + def test_add__spritelist_overriding_layer(self): + expected_layer = 33 + sprites = [] + sprite_and_layer_count = 10 + for i in range(sprite_and_layer_count): + sprites.append(self.sprite()) + sprites[-1].layer = i + + self.LG.add(sprites, layer=expected_layer) + + self.assertEqual(len(self.LG._spritelist), sprite_and_layer_count) + + for i in range(sprite_and_layer_count): + layer = self.LG.get_layer_of_sprite(sprites[i]) + + self.assertEqual(layer, expected_layer) + + def test_add__spritelist_init(self): + sprite_count = 10 + sprites = [self.sprite() for _ in range(sprite_count)] + + lrg2 = sprite.LayeredUpdates(sprites) + expected_layer = lrg2._default_layer + + self.assertEqual(len(lrg2._spritelist), sprite_count) + + for i in range(sprite_count): + layer = lrg2.get_layer_of_sprite(sprites[i]) + + self.assertEqual(layer, expected_layer) + + def test_remove__sprite(self): + sprites = [] + sprite_count = 10 + for i in range(sprite_count): + sprites.append(self.sprite()) + sprites[-1].rect = pygame.Rect((0, 0, 0, 0)) + + self.LG.add(sprites) + + self.assertEqual(len(self.LG._spritelist), sprite_count) + + for i in range(sprite_count): + self.LG.remove(sprites[i]) + + self.assertEqual(len(self.LG._spritelist), 0) + + def test_sprites(self): + sprites = [] + sprite_and_layer_count = 10 + for i in range(sprite_and_layer_count, 0, -1): + sprites.append(self.sprite()) + sprites[-1]._layer = i + + self.LG.add(sprites) + + self.assertEqual(len(self.LG._spritelist), sprite_and_layer_count) + + # Sprites should be ordered based on their layer (bottom to top), + # which is the reverse order of the sprites list. + expected_sprites = list(reversed(sprites)) + actual_sprites = self.LG.sprites() + + self.assertListEqual(actual_sprites, expected_sprites) + + def test_layers(self): + sprites = [] + expected_layers = [] + layer_count = 10 + for i in range(layer_count): + expected_layers.append(i) + for j in range(5): + sprites.append(self.sprite()) + sprites[-1]._layer = i + self.LG.add(sprites) + + layers = self.LG.layers() + + self.assertListEqual(layers, expected_layers) + + def test_add__layers_are_correct(self): + layers = [1, 4, 6, 8, 3, 6, 2, 6, 4, 5, 6, 1, 0, 9, 7, 6, 54, 8, 2, 43, 6, 1] + for lay in layers: + self.LG.add(self.sprite(), layer=lay) + layers.sort() + + for idx, spr in enumerate(self.LG.sprites()): + layer = self.LG.get_layer_of_sprite(spr) + + self.assertEqual(layer, layers[idx]) + + def test_change_layer(self): + expected_layer = 99 + spr = self.sprite() + self.LG.add(spr, layer=expected_layer) + + self.assertEqual(self.LG._spritelayers[spr], expected_layer) + + expected_layer = 44 + self.LG.change_layer(spr, expected_layer) + + self.assertEqual(self.LG._spritelayers[spr], expected_layer) + + expected_layer = 77 + spr2 = self.sprite() + spr2.layer = 55 + self.LG.add(spr2) + self.LG.change_layer(spr2, expected_layer) + + self.assertEqual(spr2.layer, expected_layer) + + def test_get_sprites_at(self): + sprites = [] + expected_sprites = [] + for i in range(3): + spr = self.sprite() + spr.rect = pygame.Rect(i * 50, i * 50, 100, 100) + sprites.append(spr) + if i < 2: + expected_sprites.append(spr) + self.LG.add(sprites) + result = self.LG.get_sprites_at((50, 50)) + self.assertEqual(result, expected_sprites) + + + def test_get_top_layer(self): + layers = [1, 5, 2, 8, 4, 5, 3, 88, 23, 0] + for i in layers: + self.LG.add(self.sprite(), layer=i) + top_layer = self.LG.get_top_layer() + + self.assertEqual(top_layer, self.LG.get_top_layer()) + self.assertEqual(top_layer, max(layers)) + self.assertEqual(top_layer, max(self.LG._spritelayers.values())) + self.assertEqual(top_layer, self.LG._spritelayers[self.LG._spritelist[-1]]) + + def test_get_bottom_layer(self): + layers = [1, 5, 2, 8, 4, 5, 3, 88, 23, 0] + for i in layers: + self.LG.add(self.sprite(), layer=i) + bottom_layer = self.LG.get_bottom_layer() + + self.assertEqual(bottom_layer, self.LG.get_bottom_layer()) + self.assertEqual(bottom_layer, min(layers)) + self.assertEqual(bottom_layer, min(self.LG._spritelayers.values())) + self.assertEqual(bottom_layer, self.LG._spritelayers[self.LG._spritelist[0]]) + + def test_move_to_front(self): + layers = [1, 5, 2, 8, 4, 5, 3, 88, 23, 0] + for i in layers: + self.LG.add(self.sprite(), layer=i) + spr = self.sprite() + self.LG.add(spr, layer=3) + + self.assertNotEqual(spr, self.LG._spritelist[-1]) + + self.LG.move_to_front(spr) + + self.assertEqual(spr, self.LG._spritelist[-1]) + + def test_move_to_back(self): + layers = [1, 5, 2, 8, 4, 5, 3, 88, 23, 0] + for i in layers: + self.LG.add(self.sprite(), layer=i) + spr = self.sprite() + self.LG.add(spr, layer=55) + + self.assertNotEqual(spr, self.LG._spritelist[0]) + + self.LG.move_to_back(spr) + + self.assertEqual(spr, self.LG._spritelist[0]) + + def test_get_top_sprite(self): + layers = [1, 5, 2, 8, 4, 5, 3, 88, 23, 0] + for i in layers: + self.LG.add(self.sprite(), layer=i) + expected_layer = self.LG.get_top_layer() + layer = self.LG.get_layer_of_sprite(self.LG.get_top_sprite()) + + self.assertEqual(layer, expected_layer) + + def test_get_sprites_from_layer(self): + sprites = {} + layers = [ + 1, + 4, + 5, + 6, + 3, + 7, + 8, + 2, + 1, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 0, + 1, + 6, + 5, + 4, + 3, + 2, + ] + for lay in layers: + spr = self.sprite() + spr._layer = lay + self.LG.add(spr) + if lay not in sprites: + sprites[lay] = [] + sprites[lay].append(spr) + + for lay in self.LG.layers(): + for spr in self.LG.get_sprites_from_layer(lay): + self.assertIn(spr, sprites[lay]) + + sprites[lay].remove(spr) + if len(sprites[lay]) == 0: + del sprites[lay] + + self.assertEqual(len(sprites.values()), 0) + + def test_switch_layer(self): + sprites1 = [] + sprites2 = [] + layers = [3, 2, 3, 2, 3, 3, 2, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 3, 2, 2, 3, 2, 3] + for lay in layers: + spr = self.sprite() + spr._layer = lay + self.LG.add(spr) + if lay == 2: + sprites1.append(spr) + else: + sprites2.append(spr) + + sprites1.sort(key=id) + sprites2.sort(key=id) + layer2_sprites = sorted(self.LG.get_sprites_from_layer(2), key=id) + layer3_sprites = sorted(self.LG.get_sprites_from_layer(3), key=id) + + self.assertListEqual(sprites1, layer2_sprites) + self.assertListEqual(sprites2, layer3_sprites) + self.assertEqual(len(self.LG), len(sprites1) + len(sprites2)) + + self.LG.switch_layer(2, 3) + layer2_sprites = sorted(self.LG.get_sprites_from_layer(2), key=id) + layer3_sprites = sorted(self.LG.get_sprites_from_layer(3), key=id) + + self.assertListEqual(sprites1, layer3_sprites) + self.assertListEqual(sprites2, layer2_sprites) + self.assertEqual(len(self.LG), len(sprites1) + len(sprites2)) + + def test_copy(self): + self.LG.add(self.sprite()) + spr = self.LG.sprites()[0] + lg_copy = self.LG.copy() + + self.assertIsInstance(lg_copy, type(self.LG)) + self.assertIn(spr, lg_copy) + self.assertIn(lg_copy, spr.groups()) + + +########################## LAYERED RENDER GROUP TESTS ########################## + + +class LayeredUpdatesTypeTest__SpriteTest(LayeredGroupBase, unittest.TestCase): + sprite = sprite.Sprite + + def setUp(self): + self.LG = sprite.LayeredUpdates() + + +class LayeredUpdatesTypeTest__DirtySprite(LayeredGroupBase, unittest.TestCase): + sprite = sprite.DirtySprite + + def setUp(self): + self.LG = sprite.LayeredUpdates() + + +class LayeredDirtyTypeTest__DirtySprite(LayeredGroupBase, unittest.TestCase): + sprite = sprite.DirtySprite + + def setUp(self): + self.LG = sprite.LayeredDirty() + + def test_repaint_rect(self): + group = self.LG + surface = pygame.Surface((100, 100)) + + group.repaint_rect(pygame.Rect(0, 0, 100, 100)) + group.draw(surface) + + def test_repaint_rect_with_clip(self): + group = self.LG + surface = pygame.Surface((100, 100)) + + group.set_clip(pygame.Rect(0, 0, 100, 100)) + group.repaint_rect(pygame.Rect(0, 0, 100, 100)) + group.draw(surface) + + def _nondirty_intersections_redrawn(self, use_source_rect=False): + # Helper method to ensure non-dirty sprites are redrawn correctly. + # + # Parameters: + # use_source_rect - allows non-dirty sprites to be tested + # with (True) and without (False) a source_rect + # + # This test was written to reproduce the behavior seen in issue #898. + # A non-dirty sprite (using source_rect) was being redrawn incorrectly + # after a dirty sprite intersected with it. + # + # This test does the following. + # 1. Creates a surface filled with white. Also creates an image_source + # with a default fill color of yellow and adds 2 images to it + # (red and blue rectangles). + # 2. Creates 2 DirtySprites (red_sprite and blue_sprite) using the + # image_source and adds them to a LayeredDirty group. + # 3. Moves the red_sprite and calls LayeredDirty.draw(surface) a few + # times. + # 4. Checks to make sure the sprites were redrawn correctly. + RED = pygame.Color("red") + BLUE = pygame.Color("blue") + WHITE = pygame.Color("white") + YELLOW = pygame.Color("yellow") + + surface = pygame.Surface((60, 80)) + surface.fill(WHITE) + start_pos = (10, 10) + + # These rects define each sprite's image area in the image_source. + red_sprite_source = pygame.Rect((45, 0), (5, 4)) + blue_sprite_source = pygame.Rect((0, 40), (20, 10)) + + # Create a source image/surface. + image_source = pygame.Surface((50, 50)) + image_source.fill(YELLOW) + image_source.fill(RED, red_sprite_source) + image_source.fill(BLUE, blue_sprite_source) + + # The blue_sprite is stationary and will not reset its dirty flag. It + # will be the non-dirty sprite in this test. Its values are dependent + # on the use_source_rect flag. + blue_sprite = pygame.sprite.DirtySprite(self.LG) + + if use_source_rect: + blue_sprite.image = image_source + # The rect is a bit smaller than the source_rect to make sure + # LayeredDirty.draw() is using the correct dimensions. + blue_sprite.rect = pygame.Rect( + start_pos, (blue_sprite_source.w - 7, blue_sprite_source.h - 7) + ) + blue_sprite.source_rect = blue_sprite_source + start_x, start_y = blue_sprite.rect.topleft + end_x = start_x + blue_sprite.source_rect.w + end_y = start_y + blue_sprite.source_rect.h + else: + blue_sprite.image = image_source.subsurface(blue_sprite_source) + blue_sprite.rect = pygame.Rect(start_pos, blue_sprite_source.size) + start_x, start_y = blue_sprite.rect.topleft + end_x, end_y = blue_sprite.rect.bottomright + + # The red_sprite is moving and will always be dirty. + red_sprite = pygame.sprite.DirtySprite(self.LG) + red_sprite.image = image_source + red_sprite.rect = pygame.Rect(start_pos, red_sprite_source.size) + red_sprite.source_rect = red_sprite_source + red_sprite.dirty = 2 + + # Draw the red_sprite as it moves a few steps. + for _ in range(4): + red_sprite.rect.move_ip(2, 1) + + # This is the method being tested. + self.LG.draw(surface) + + # Check colors where the blue_sprite is drawn. We expect red where the + # red_sprite is drawn over the blue_sprite, but the rest should be + # blue. + surface.lock() # Lock surface for possible speed up. + try: + for y in range(start_y, end_y): + for x in range(start_x, end_x): + if red_sprite.rect.collidepoint(x, y): + expected_color = RED + else: + expected_color = BLUE + + color = surface.get_at((x, y)) + + self.assertEqual(color, expected_color, "pos=({}, {})".format(x, y)) + finally: + surface.unlock() + + def test_nondirty_intersections_redrawn(self): + """Ensure non-dirty sprites are correctly redrawn + when dirty sprites intersect with them. + """ + self._nondirty_intersections_redrawn() + + def test_nondirty_intersections_redrawn__with_source_rect(self): + """Ensure non-dirty sprites using source_rects are correctly redrawn + when dirty sprites intersect with them. + + Related to issue #898. + """ + self._nondirty_intersections_redrawn(True) + + +############################### SPRITE BASE CLASS ############################## +# +# tests common between sprite classes + + +class SpriteBase: + def setUp(self): + self.groups = [] + for Group in self.Groups: + self.groups.append(Group()) + + self.sprite = self.Sprite() + + def test_add_internal(self): + + for g in self.groups: + self.sprite.add_internal(g) + + for g in self.groups: + self.assertIn(g, self.sprite.groups()) + + def test_remove_internal(self): + + for g in self.groups: + self.sprite.add_internal(g) + + for g in self.groups: + self.sprite.remove_internal(g) + + for g in self.groups: + self.assertFalse(g in self.sprite.groups()) + + def test_update(self): + class test_sprite(pygame.sprite.Sprite): + sink = [] + + def __init__(self, *groups): + pygame.sprite.Sprite.__init__(self, *groups) + + def update(self, *args): + self.sink += args + + s = test_sprite() + s.update(1, 2, 3) + + self.assertEqual(test_sprite.sink, [1, 2, 3]) + + def test_update_with_kwargs(self): + class test_sprite(pygame.sprite.Sprite): + sink = [] + sink_dict = {} + + def __init__(self, *groups): + pygame.sprite.Sprite.__init__(self, *groups) + + def update(self, *args, **kwargs): + self.sink += args + self.sink_dict.update(kwargs) + + s = test_sprite() + s.update(1, 2, 3, foo=4, bar=5) + + self.assertEqual(test_sprite.sink, [1, 2, 3]) + self.assertEqual(test_sprite.sink_dict, {"foo": 4, "bar": 5}) + + def test___init____added_to_groups_passed(self): + expected_groups = sorted(self.groups, key=id) + sprite = self.Sprite(self.groups) + groups = sorted(sprite.groups(), key=id) + + self.assertListEqual(groups, expected_groups) + + def test_add(self): + expected_groups = sorted(self.groups, key=id) + self.sprite.add(self.groups) + groups = sorted(self.sprite.groups(), key=id) + + self.assertListEqual(groups, expected_groups) + + def test_alive(self): + self.assertFalse( + self.sprite.alive(), "Sprite should not be alive if in no groups" + ) + + self.sprite.add(self.groups) + + self.assertTrue(self.sprite.alive()) + + def test_groups(self): + for i, g in enumerate(self.groups): + expected_groups = sorted(self.groups[: i + 1], key=id) + self.sprite.add(g) + groups = sorted(self.sprite.groups(), key=id) + + self.assertListEqual(groups, expected_groups) + + def test_kill(self): + self.sprite.add(self.groups) + + self.assertTrue(self.sprite.alive()) + + self.sprite.kill() + + self.assertListEqual(self.sprite.groups(), []) + self.assertFalse(self.sprite.alive()) + + def test_remove(self): + self.sprite.add(self.groups) + self.sprite.remove(self.groups) + + self.assertListEqual(self.sprite.groups(), []) + + +############################## SPRITE CLASS TESTS ############################## + + +class SpriteTypeTest(SpriteBase, unittest.TestCase): + Sprite = sprite.Sprite + + Groups = [ + sprite.Group, + sprite.LayeredUpdates, + sprite.RenderUpdates, + sprite.OrderedUpdates, + ] + + +class DirtySpriteTypeTest(SpriteBase, unittest.TestCase): + Sprite = sprite.DirtySprite + + Groups = [ + sprite.Group, + sprite.LayeredUpdates, + sprite.RenderUpdates, + sprite.OrderedUpdates, + sprite.LayeredDirty, + ] + + +############################## BUG TESTS ####################################### + + +class SingleGroupBugsTest(unittest.TestCase): + def test_memoryleak_bug(self): + # For memoryleak bug posted to mailing list by Tobias Steinrücken on 16/11/10. + # Fixed in revision 2953. + + import weakref + import gc + + class MySprite(sprite.Sprite): + def __init__(self, *args, **kwargs): + sprite.Sprite.__init__(self, *args, **kwargs) + self.image = pygame.Surface((2, 4), 0, 24) + self.rect = self.image.get_rect() + + g = sprite.GroupSingle() + screen = pygame.Surface((4, 8), 0, 24) + s = MySprite() + r = weakref.ref(s) + g.sprite = s + del s + gc.collect() + + self.assertIsNotNone(r()) + + g.update() + g.draw(screen) + g.sprite = MySprite() + gc.collect() + + self.assertIsNone(r()) + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/surface_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/surface_test.py new file mode 100644 index 0000000..13904e6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/surface_test.py @@ -0,0 +1,2634 @@ +import os + +import unittest +from pygame.tests import test_utils +from pygame.tests.test_utils import ( + example_path, + AssertRaisesRegexMixin, + SurfaceSubclass, +) + +try: + from pygame.tests.test_utils.arrinter import * +except (ImportError, NameError): + pass + +import pygame +from pygame.locals import * +from pygame.compat import xrange_, as_bytes, as_unicode +from pygame.bufferproxy import BufferProxy + +import platform +import gc +import weakref +import ctypes + +IS_PYPY = "PyPy" == platform.python_implementation() + + +def intify(i): + """If i is a long, cast to an int while preserving the bits""" + if 0x80000000 & i: + return int((0xFFFFFFFF & i)) + return i + + +def longify(i): + """If i is an int, cast to a long while preserving the bits""" + if i < 0: + return 0xFFFFFFFF & i + return long(i) + + +class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): + def test_surface__pixel_format_as_surface_subclass(self): + """Ensure a subclassed surface can be used for pixel format + when creating a new surface.""" + expected_depth = 16 + expected_flags = SRCALPHA + expected_size = (13, 37) + depth_surface = SurfaceSubclass((11, 21), expected_flags, expected_depth) + + surface = pygame.Surface(expected_size, 0, depth_surface) + + self.assertIsNot(surface, depth_surface) + self.assertIsInstance(surface, pygame.Surface) + self.assertNotIsInstance(surface, SurfaceSubclass) + self.assertEqual(surface.get_size(), expected_size) + self.assertEqual(surface.get_flags(), expected_flags) + self.assertEqual(surface.get_bitsize(), expected_depth) + + def test_set_clip(self): + """ see if surface.set_clip(None) works correctly. + """ + s = pygame.Surface((800, 600)) + r = pygame.Rect(10, 10, 10, 10) + s.set_clip(r) + r.move_ip(10, 0) + s.set_clip(None) + res = s.get_clip() + # this was garbled before. + self.assertEqual(res[0], 0) + self.assertEqual(res[2], 800) + + def test_print(self): + surf = pygame.Surface((70, 70), 0, 32) + self.assertEqual(repr(surf), "") + + def test_keyword_arguments(self): + surf = pygame.Surface((70, 70), flags=SRCALPHA, depth=32) + self.assertEqual(surf.get_flags() & SRCALPHA, SRCALPHA) + self.assertEqual(surf.get_bitsize(), 32) + + # sanity check to make sure the check below is valid + surf_16 = pygame.Surface((70, 70), 0, 16) + self.assertEqual(surf_16.get_bytesize(), 2) + + # try again with an argument list + surf_16 = pygame.Surface((70, 70), depth=16) + self.assertEqual(surf_16.get_bytesize(), 2) + + def test_set_at(self): + + # 24bit surfaces + s = pygame.Surface((100, 100), 0, 24) + s.fill((0, 0, 0)) + + # set it with a tuple. + s.set_at((0, 0), (10, 10, 10, 255)) + r = s.get_at((0, 0)) + self.assertIsInstance(r, pygame.Color) + self.assertEqual(r, (10, 10, 10, 255)) + + # try setting a color with a single integer. + s.fill((0, 0, 0, 255)) + s.set_at((10, 1), 0x0000FF) + r = s.get_at((10, 1)) + self.assertEqual(r, (0, 0, 255, 255)) + + def test_SRCALPHA(self): + # has the flag been passed in ok? + surf = pygame.Surface((70, 70), SRCALPHA, 32) + self.assertEqual(surf.get_flags() & SRCALPHA, SRCALPHA) + + # 24bit surfaces can not have SRCALPHA. + self.assertRaises(ValueError, pygame.Surface, (100, 100), pygame.SRCALPHA, 24) + + # if we have a 32 bit surface, the SRCALPHA should have worked too. + surf2 = pygame.Surface((70, 70), SRCALPHA) + if surf2.get_bitsize() == 32: + self.assertEqual(surf2.get_flags() & SRCALPHA, SRCALPHA) + + def test_masks(self): + def make_surf(bpp, flags, masks): + pygame.Surface((10, 10), flags, bpp, masks) + + # With some masks SDL_CreateRGBSurface does not work properly. + masks = (0xFF000000, 0xFF0000, 0xFF00, 0) + self.assertEqual(make_surf(32, 0, masks), None) + # For 24 and 32 bit surfaces Pygame assumes no losses. + masks = (0x7F0000, 0xFF00, 0xFF, 0) + self.assertRaises(ValueError, make_surf, 24, 0, masks) + self.assertRaises(ValueError, make_surf, 32, 0, masks) + # What contiguous bits in a mask. + masks = (0x6F0000, 0xFF00, 0xFF, 0) + self.assertRaises(ValueError, make_surf, 32, 0, masks) + + def test_get_bounding_rect(self): + surf = pygame.Surface((70, 70), SRCALPHA, 32) + surf.fill((0, 0, 0, 0)) + bound_rect = surf.get_bounding_rect() + self.assertEqual(bound_rect.width, 0) + self.assertEqual(bound_rect.height, 0) + surf.set_at((30, 30), (255, 255, 255, 1)) + bound_rect = surf.get_bounding_rect() + self.assertEqual(bound_rect.left, 30) + self.assertEqual(bound_rect.top, 30) + self.assertEqual(bound_rect.width, 1) + self.assertEqual(bound_rect.height, 1) + surf.set_at((29, 29), (255, 255, 255, 1)) + bound_rect = surf.get_bounding_rect() + self.assertEqual(bound_rect.left, 29) + self.assertEqual(bound_rect.top, 29) + self.assertEqual(bound_rect.width, 2) + self.assertEqual(bound_rect.height, 2) + + surf = pygame.Surface((70, 70), 0, 24) + surf.fill((0, 0, 0)) + bound_rect = surf.get_bounding_rect() + self.assertEqual(bound_rect.width, surf.get_width()) + self.assertEqual(bound_rect.height, surf.get_height()) + + surf.set_colorkey((0, 0, 0)) + bound_rect = surf.get_bounding_rect() + self.assertEqual(bound_rect.width, 0) + self.assertEqual(bound_rect.height, 0) + surf.set_at((30, 30), (255, 255, 255)) + bound_rect = surf.get_bounding_rect() + self.assertEqual(bound_rect.left, 30) + self.assertEqual(bound_rect.top, 30) + self.assertEqual(bound_rect.width, 1) + self.assertEqual(bound_rect.height, 1) + surf.set_at((60, 60), (255, 255, 255)) + bound_rect = surf.get_bounding_rect() + self.assertEqual(bound_rect.left, 30) + self.assertEqual(bound_rect.top, 30) + self.assertEqual(bound_rect.width, 31) + self.assertEqual(bound_rect.height, 31) + + # Issue #180 + pygame.display.init() + try: + surf = pygame.Surface((4, 1), 0, 8) + surf.fill((255, 255, 255)) + surf.get_bounding_rect() # Segfault. + finally: + pygame.display.quit() + + def test_copy(self): + """Ensure a surface can be copied.""" + color = (25, 25, 25, 25) + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32) + s1.fill(color) + + s2 = s1.copy() + + s1rect = s1.get_rect() + s2rect = s2.get_rect() + + self.assertEqual(s1rect.size, s2rect.size) + self.assertEqual(s2.get_at((10, 10)), color) + + def test_fill(self): + """Ensure a surface can be filled.""" + color = (25, 25, 25, 25) + fill_rect = pygame.Rect(0, 0, 16, 16) + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32) + s1.fill(color, fill_rect) + + for pt in test_utils.rect_area_pts(fill_rect): + self.assertEqual(s1.get_at(pt), color) + + for pt in test_utils.rect_outer_bounds(fill_rect): + self.assertNotEqual(s1.get_at(pt), color) + + def test_fill_rle(self): + color = (250, 25, 25, 255) + color2 = (200, 200, 250, 255) + sub_rect = pygame.Rect(16, 16, 16, 16) + s0 = pygame.Surface((32, 32), 24) + s1 = pygame.Surface((32, 32), 24) + s1.set_colorkey((255,0,255), pygame.RLEACCEL) + s0.blit(s1, (0,0)) + s1.fill(color) + self.assertTrue(s1.get_flags()&pygame.RLEACCEL) + + @unittest.expectedFailure + def test_copy_rle(self): + color = (250, 25, 25, 255) + color2 = (200, 200, 250, 255) + sub_rect = pygame.Rect(16, 16, 16, 16) + s0 = pygame.Surface((32, 32), 24) + s1 = pygame.Surface((32, 32), 24) + s1.set_colorkey((255,0,255), pygame.RLEACCEL) + s0.blit(s1, (0,0)) + s1.copy() + self.assertTrue(s1.get_flags()&pygame.RLEACCEL) + + def test_subsurface_rle(self): + """Ensure an RLE sub-surface works independently of its parent.""" + color = (250, 25, 25, 255) + color2 = (200, 200, 250, 255) + sub_rect = pygame.Rect(16, 16, 16, 16) + s0 = pygame.Surface((32, 32), 24) + s1 = pygame.Surface((32, 32), 24) + s1.set_colorkey((255,0,255), pygame.RLEACCEL) + s1.fill(color) + s2 = s1.subsurface(sub_rect) + s2.fill(color2) + s0.blit(s1, (0,0)) + self.assertTrue(s1.get_flags()&pygame.RLEACCEL) + self.assertTrue(not s2.get_flags()&pygame.RLEACCEL) + + @unittest.expectedFailure + def test_subsurface_rle2(self): + color = (250, 25, 25, 255) + color2 = (200, 200, 250, 255) + sub_rect = pygame.Rect(16, 16, 16, 16) + + s0 = pygame.Surface((32, 32), 24) + s1 = pygame.Surface((32, 32), 24) + s1.set_colorkey((255,0,255), pygame.RLEACCEL) + s1.fill(color) + s2 = s1.subsurface(sub_rect) + s2.fill(color2) + s0.blit(s2, (0,0)) + self.assertTrue(s1.get_flags()&pygame.RLEACCEL) + self.assertTrue(not s2.get_flags()&pygame.RLEACCEL) + + def test_fill_negative_coordinates(self): + + # negative coordinates should be clipped by fill, and not draw outside the surface. + color = (25, 25, 25, 25) + color2 = (20, 20, 20, 25) + fill_rect = pygame.Rect(-10, -10, 16, 16) + + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32) + r1 = s1.fill(color, fill_rect) + c = s1.get_at((0, 0)) + self.assertEqual(c, color) + + # make subsurface in the middle to test it doesn't over write. + s2 = s1.subsurface((5, 5, 5, 5)) + r2 = s2.fill(color2, (-3, -3, 5, 5)) + c2 = s1.get_at((4, 4)) + self.assertEqual(c, color) + + # rect returns the area we actually fill. + r3 = s2.fill(color2, (-30, -30, 5, 5)) + # since we are using negative coords, it should be an zero sized rect. + self.assertEqual(tuple(r3), (0, 0, 0, 0)) + + def test_fill_keyword_args(self): + """Ensure fill() accepts keyword arguments.""" + color = (1, 2, 3, 255) + area = (1, 1, 2, 2) + s1 = pygame.Surface((4, 4), 0, 32) + s1.fill(special_flags=pygame.BLEND_ADD, color=color, rect=area) + + self.assertEqual(s1.get_at((0, 0)), (0, 0, 0, 255)) + self.assertEqual(s1.get_at((1, 1)), color) + + ######################################################################## + + def test_get_alpha(self): + """Ensure a surface's alpha value can be retrieved.""" + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32) + + self.assertEqual(s1.get_alpha(), 255) + + for alpha in (0, 32, 127, 255): + s1.set_alpha(alpha) + for t in range(4): + s1.set_alpha(s1.get_alpha()) + + self.assertEqual(s1.get_alpha(), alpha) + + ######################################################################## + + def test_get_bytesize(self): + """Ensure a surface's bit and byte sizes can be retrieved.""" + depth = 32 + depth_bytes = 4 + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, depth) + + self.assertEqual(s1.get_bytesize(), depth_bytes) + self.assertEqual(s1.get_bitsize(), depth) + + ######################################################################## + + def test_get_flags(self): + """Ensure a surface's flags can be retrieved.""" + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32) + + self.assertEqual(s1.get_flags(), pygame.SRCALPHA) + + ######################################################################## + + def test_get_parent(self): + """Ensure a surface's parent can be retrieved.""" + parent = pygame.Surface((16, 16)) + child = parent.subsurface((0, 0, 5, 5)) + + self.assertIs(child.get_parent(), parent) + + ######################################################################## + + def test_get_rect(self): + """Ensure a surface's rect can be retrieved.""" + size = (16, 16) + surf = pygame.Surface(size) + rect = surf.get_rect() + + self.assertEqual(rect.size, size) + + ######################################################################## + + def test_get_width__size_and_height(self): + """Ensure a surface's size, width and height can be retrieved.""" + for w in xrange_(0, 255, 32): + for h in xrange_(0, 127, 15): + s = pygame.Surface((w, h)) + self.assertEqual(s.get_width(), w) + self.assertEqual(s.get_height(), h) + self.assertEqual(s.get_size(), (w, h)) + + def test_get_view(self): + """Ensure a buffer view of the surface's pixels can be retrieved.""" + # Check that BufferProxys are returned when array depth is supported, + # ValueErrors returned otherwise. + Error = ValueError + s = pygame.Surface((5, 7), 0, 8) + v2 = s.get_view("2") + + self.assertRaises(Error, s.get_view, "0") + self.assertRaises(Error, s.get_view, "1") + self.assertIsInstance(v2, BufferProxy) + self.assertRaises(Error, s.get_view, "3") + + s = pygame.Surface((8, 7), 0, 8) + length = s.get_bytesize() * s.get_width() * s.get_height() + v0 = s.get_view("0") + v1 = s.get_view("1") + + self.assertIsInstance(v0, BufferProxy) + self.assertEqual(v0.length, length) + self.assertIsInstance(v1, BufferProxy) + self.assertEqual(v1.length, length) + + s = pygame.Surface((5, 7), 0, 16) + v2 = s.get_view("2") + + self.assertRaises(Error, s.get_view, "0") + self.assertRaises(Error, s.get_view, "1") + self.assertIsInstance(v2, BufferProxy) + self.assertRaises(Error, s.get_view, "3") + + s = pygame.Surface((8, 7), 0, 16) + length = s.get_bytesize() * s.get_width() * s.get_height() + v0 = s.get_view("0") + v1 = s.get_view("1") + + self.assertIsInstance(v0, BufferProxy) + self.assertEqual(v0.length, length) + self.assertIsInstance(v1, BufferProxy) + self.assertEqual(v1.length, length) + + s = pygame.Surface((5, 7), pygame.SRCALPHA, 16) + v2 = s.get_view("2") + + self.assertIsInstance(v2, BufferProxy) + self.assertRaises(Error, s.get_view, "3") + + s = pygame.Surface((5, 7), 0, 24) + v2 = s.get_view("2") + v3 = s.get_view("3") + + self.assertRaises(Error, s.get_view, "0") + self.assertRaises(Error, s.get_view, "1") + self.assertIsInstance(v2, BufferProxy) + self.assertIsInstance(v3, BufferProxy) + + s = pygame.Surface((8, 7), 0, 24) + length = s.get_bytesize() * s.get_width() * s.get_height() + v0 = s.get_view("0") + v1 = s.get_view("1") + + self.assertIsInstance(v0, BufferProxy) + self.assertEqual(v0.length, length) + self.assertIsInstance(v1, BufferProxy) + self.assertEqual(v1.length, length) + + s = pygame.Surface((5, 7), 0, 32) + length = s.get_bytesize() * s.get_width() * s.get_height() + v0 = s.get_view("0") + v1 = s.get_view("1") + v2 = s.get_view("2") + v3 = s.get_view("3") + + self.assertIsInstance(v0, BufferProxy) + self.assertEqual(v0.length, length) + self.assertIsInstance(v1, BufferProxy) + self.assertEqual(v1.length, length) + self.assertIsInstance(v2, BufferProxy) + self.assertIsInstance(v3, BufferProxy) + + s2 = s.subsurface((0, 0, 4, 7)) + + self.assertRaises(Error, s2.get_view, "0") + self.assertRaises(Error, s2.get_view, "1") + + s2 = None + s = pygame.Surface((5, 7), pygame.SRCALPHA, 32) + + for kind in ("2", "3", "a", "A", "r", "R", "g", "G", "b", "B"): + self.assertIsInstance(s.get_view(kind), BufferProxy) + + # Check default argument value: '2' + s = pygame.Surface((2, 4), 0, 32) + v = s.get_view() + if not IS_PYPY: + ai = ArrayInterface(v) + self.assertEqual(ai.nd, 2) + + # Check locking. + s = pygame.Surface((2, 4), 0, 32) + + self.assertFalse(s.get_locked()) + + v = s.get_view("2") + + self.assertFalse(s.get_locked()) + + c = v.__array_interface__ + + self.assertTrue(s.get_locked()) + + c = None + gc.collect() + + self.assertTrue(s.get_locked()) + + v = None + gc.collect() + + self.assertFalse(s.get_locked()) + + # Check invalid view kind values. + s = pygame.Surface((2, 4), pygame.SRCALPHA, 32) + self.assertRaises(TypeError, s.get_view, "") + self.assertRaises(TypeError, s.get_view, "9") + self.assertRaises(TypeError, s.get_view, "RGBA") + self.assertRaises(TypeError, s.get_view, 2) + + # Both unicode and bytes strings are allowed for kind. + s = pygame.Surface((2, 4), 0, 32) + s.get_view(as_unicode("2")) + s.get_view(as_bytes("2")) + + # Garbage collection + s = pygame.Surface((2, 4), 0, 32) + weak_s = weakref.ref(s) + v = s.get_view("3") + weak_v = weakref.ref(v) + gc.collect() + self.assertTrue(weak_s() is s) + self.assertTrue(weak_v() is v) + del v + gc.collect() + self.assertTrue(weak_s() is s) + self.assertTrue(weak_v() is None) + del s + gc.collect() + self.assertTrue(weak_s() is None) + + def test_get_buffer(self): + # Check that get_buffer works for all pixel sizes and for a subsurface. + + # Check for all pixel sizes + for bitsize in [8, 16, 24, 32]: + s = pygame.Surface((5, 7), 0, bitsize) + length = s.get_pitch() * s.get_height() + v = s.get_buffer() + + self.assertIsInstance(v, BufferProxy) + self.assertEqual(v.length, length) + self.assertEqual(repr(v), "") + + # Check for a subsurface (not contiguous) + s = pygame.Surface((7, 10), 0, 32) + s2 = s.subsurface((1, 2, 5, 7)) + length = s2.get_pitch() * s2.get_height() + v = s2.get_buffer() + + self.assertIsInstance(v, BufferProxy) + self.assertEqual(v.length, length) + + # Check locking. + s = pygame.Surface((2, 4), 0, 32) + v = s.get_buffer() + self.assertTrue(s.get_locked()) + v = None + gc.collect() + self.assertFalse(s.get_locked()) + + OLDBUF = hasattr(pygame.bufferproxy, "get_segcount") + + @unittest.skipIf(not OLDBUF, "old buffer not available") + def test_get_buffer_oldbuf(self): + from pygame.bufferproxy import get_segcount, get_write_buffer + + s = pygame.Surface((2, 4), pygame.SRCALPHA, 32) + v = s.get_buffer() + segcount, buflen = get_segcount(v) + self.assertEqual(segcount, 1) + self.assertEqual(buflen, s.get_pitch() * s.get_height()) + seglen, segaddr = get_write_buffer(v, 0) + self.assertEqual(segaddr, s._pixels_address) + self.assertEqual(seglen, buflen) + + @unittest.skipIf(not OLDBUF, "old buffer not available") + def test_get_view_oldbuf(self): + from pygame.bufferproxy import get_segcount, get_write_buffer + + s = pygame.Surface((2, 4), pygame.SRCALPHA, 32) + v = s.get_view("1") + segcount, buflen = get_segcount(v) + self.assertEqual(segcount, 8) + self.assertEqual(buflen, s.get_pitch() * s.get_height()) + seglen, segaddr = get_write_buffer(v, 7) + self.assertEqual(segaddr, s._pixels_address + s.get_bytesize() * 7) + self.assertEqual(seglen, s.get_bytesize()) + + def test_set_colorkey(self): + + # __doc__ (as of 2008-06-25) for pygame.surface.Surface.set_colorkey: + + # Surface.set_colorkey(Color, flags=0): return None + # Surface.set_colorkey(None): return None + # Set the transparent colorkey + + s = pygame.Surface((16, 16), pygame.SRCALPHA, 32) + + colorkeys = ((20, 189, 20, 255), (128, 50, 50, 255), (23, 21, 255, 255)) + + for colorkey in colorkeys: + s.set_colorkey(colorkey) + + for t in range(4): + s.set_colorkey(s.get_colorkey()) + + self.assertEqual(s.get_colorkey(), colorkey) + + def test_set_masks(self): + s = pygame.Surface((32, 32)) + r, g, b, a = s.get_masks() + if pygame.get_sdl_version()[0] == 1: + s.set_masks((b, g, r, a)) + r2, g2, b2, a2 = s.get_masks() + self.assertEqual((r, g, b, a), (b2, g2, r2, a2)) + else: + self.assertRaises(TypeError, s.set_masks, (b, g, r, a)) + + def test_set_shifts(self): + s = pygame.Surface((32, 32)) + r, g, b, a = s.get_shifts() + if pygame.get_sdl_version()[0] == 1: + s.set_shifts((b, g, r, a)) + r2, g2, b2, a2 = s.get_shifts() + self.assertEqual((r, g, b, a), (b2, g2, r2, a2)) + else: + self.assertRaises(TypeError, s.set_shifts, (b, g, r, a)) + + def test_blit_keyword_args(self): + color = (1, 2, 3, 255) + s1 = pygame.Surface((4, 4), 0, 32) + s2 = pygame.Surface((2, 2), 0, 32) + s2.fill((1, 2, 3)) + s1.blit(special_flags=BLEND_ADD, source=s2, dest=(1, 1), area=s2.get_rect()) + self.assertEqual(s1.get_at((0, 0)), (0, 0, 0, 255)) + self.assertEqual(s1.get_at((1, 1)), color) + + def todo_test_blit(self): + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.blit: + + # Surface.blit(source, dest, area=None, special_flags = 0): return Rect + # draw one image onto another + # + # Draws a source Surface onto this Surface. The draw can be positioned + # with the dest argument. Dest can either be pair of coordinates + # representing the upper left corner of the source. A Rect can also be + # passed as the destination and the topleft corner of the rectangle + # will be used as the position for the blit. The size of the + # destination rectangle does not effect the blit. + # + # An optional area rectangle can be passed as well. This represents a + # smaller portion of the source Surface to draw. + # + # An optional special flags is for passing in new in 1.8.0: BLEND_ADD, + # BLEND_SUB, BLEND_MULT, BLEND_MIN, BLEND_MAX new in 1.8.1: + # BLEND_RGBA_ADD, BLEND_RGBA_SUB, BLEND_RGBA_MULT, BLEND_RGBA_MIN, + # BLEND_RGBA_MAX BLEND_RGB_ADD, BLEND_RGB_SUB, BLEND_RGB_MULT, + # BLEND_RGB_MIN, BLEND_RGB_MAX With other special blitting flags + # perhaps added in the future. + # + # The return rectangle is the area of the affected pixels, excluding + # any pixels outside the destination Surface, or outside the clipping + # area. + # + # Pixel alphas will be ignored when blitting to an 8 bit Surface. + # special_flags new in pygame 1.8. + + self.fail() + + def test_blit__SRCALPHA_opaque_source(self): + src = pygame.Surface((256, 256), SRCALPHA, 32) + dst = src.copy() + + for i, j in test_utils.rect_area_pts(src.get_rect()): + dst.set_at((i, j), (i, 0, 0, j)) + src.set_at((i, j), (0, i, 0, 255)) + + dst.blit(src, (0, 0)) + + for pt in test_utils.rect_area_pts(src.get_rect()): + self.assertEqual(dst.get_at(pt)[1], src.get_at(pt)[1]) + + def todo_test_blit__blit_to_self(self): # TODO + src = pygame.Surface((256, 256), SRCALPHA, 32) + rect = src.get_rect() + + for pt, color in test_utils.gradient(rect.width, rect.height): + src.set_at(pt, color) + + src.blit(src, (0, 0)) + + def todo_test_blit__SRCALPHA_to_SRCALPHA_non_zero(self): # TODO + # " There is no unit test for blitting a SRCALPHA source with non-zero + # alpha to a SRCALPHA destination with non-zero alpha " LL + + w, h = size = 32, 32 + + s = pygame.Surface(size, pygame.SRCALPHA, 32) + s2 = s.copy() + + s.fill((32, 32, 32, 111)) + s2.fill((32, 32, 32, 31)) + + s.blit(s2, (0, 0)) + + # TODO: + # what is the correct behaviour ?? should it blend? what algorithm? + + self.assertEqual(s.get_at((0, 0)), (32, 32, 32, 31)) + + def test_blit__SRCALPHA32_to_8(self): + # Bug: fatal + # SDL_DisplayConvert segfaults when video is uninitialized. + target = pygame.Surface((11, 8), 0, 8) + color = target.get_palette_at(2) + source = pygame.Surface((1, 1), pygame.SRCALPHA, 32) + source.set_at((0, 0), color) + target.blit(source, (0, 0)) + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", + 'requires a non-"dummy" SDL_VIDEODRIVER', + ) + def test_image_convert_bug_131(self): + # Bitbucket bug #131: Unable to Surface.convert(32) some 1-bit images. + # https://bitbucket.org/pygame/pygame/issue/131/unable-to-surfaceconvert-32-some-1-bit + + pygame.display.init() + try: + pygame.display.set_mode((640, 480)) + + im = pygame.image.load(example_path(os.path.join("data", "city.png"))) + im2 = pygame.image.load(example_path(os.path.join("data", "brick.png"))) + + self.assertEqual(im.get_palette(), ((0, 0, 0, 255), (255, 255, 255, 255))) + self.assertEqual(im2.get_palette(), ((0, 0, 0, 255), (0, 0, 0, 255))) + + self.assertEqual(repr(im.convert(32)), "") + self.assertEqual(repr(im2.convert(32)), "") + + # Ensure a palette format to palette format works. + im3 = im.convert(8) + self.assertEqual(repr(im3), "") + self.assertEqual(im3.get_palette(), im.get_palette()) + + finally: + pygame.display.quit() + + def test_convert_init(self): + """ Ensure initialization exceptions are raised + for surf.convert().""" + pygame.display.quit() + surf = pygame.Surface((1, 1)) + + self.assertRaisesRegex(pygame.error, "display initialized", surf.convert) + + pygame.display.init() + try: + if os.environ.get("SDL_VIDEODRIVER") != "dummy": + try: + surf.convert(32) + surf.convert(pygame.Surface((1, 1))) + except pygame.error: + self.fail("convert() should not raise an exception here.") + + self.assertRaisesRegex(pygame.error, "No video mode", surf.convert) + + pygame.display.set_mode((640, 480)) + try: + surf.convert() + except pygame.error: + self.fail("convert() should not raise an exception here.") + finally: + pygame.display.quit() + + def test_convert_alpha_init(self): + """ Ensure initialization exceptions are raised + for surf.convert_alpha().""" + pygame.display.quit() + surf = pygame.Surface((1, 1)) + + self.assertRaisesRegex(pygame.error, "display initialized", surf.convert_alpha) + + pygame.display.init() + try: + self.assertRaisesRegex(pygame.error, "No video mode", surf.convert_alpha) + + pygame.display.set_mode((640, 480)) + try: + surf.convert_alpha() + except pygame.error: + self.fail("convert_alpha() should not raise an exception here.") + finally: + pygame.display.quit() + + def test_convert_alpha_SRCALPHA(self): + """ Ensure that the surface returned by surf.convert_alpha() + has alpha blending enabled""" + pygame.display.init() + try: + pygame.display.set_mode((640, 480)) + + s1 = pygame.Surface((100, 100), 0, 32) + # s2=pygame.Surface((100,100), pygame.SRCALPHA, 32) + s1_alpha = s1.convert_alpha() + self.assertEqual(s1_alpha.get_flags() & SRCALPHA, SRCALPHA) + self.assertEqual(s1_alpha.get_alpha(), 255) + finally: + pygame.display.quit() + + @unittest.skip("causes failures in other tests if run, so skip") + def test_src_alpha_issue_1289(self): + """ blit should be white. + """ + surf1 = pygame.Surface((1, 1), pygame.SRCALPHA, 32) + surf1.fill((255, 255, 255, 100)) + + surf2 = pygame.Surface((1, 1), pygame.SRCALPHA, 32) + self.assertEqual(surf2.get_at((0, 0)), (0, 0, 0, 0)) + surf2.blit(surf1, (0, 0)) + + self.assertEqual(surf1.get_at((0, 0)), (255, 255, 255, 100)) + self.assertEqual(surf2.get_at((0, 0)), (255, 255, 255, 100)) + + def todo_test_convert(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.convert: + + # Surface.convert(Surface): return Surface + # Surface.convert(depth, flags=0): return Surface + # Surface.convert(masks, flags=0): return Surface + # Surface.convert(): return Surface + # change the pixel format of an image + # + # Creates a new copy of the Surface with the pixel format changed. The + # new pixel format can be determined from another existing Surface. + # Otherwise depth, flags, and masks arguments can be used, similar to + # the pygame.Surface() call. + # + # If no arguments are passed the new Surface will have the same pixel + # format as the display Surface. This is always the fastest format for + # blitting. It is a good idea to convert all Surfaces before they are + # blitted many times. + # + # The converted Surface will have no pixel alphas. They will be + # stripped if the original had them. See Surface.convert_alpha() for + # preserving or creating per-pixel alphas. + # + + self.fail() + + def test_convert__pixel_format_as_surface_subclass(self): + """Ensure convert accepts a Surface subclass argument.""" + expected_size = (23, 17) + convert_surface = SurfaceSubclass(expected_size, 0, 32) + depth_surface = SurfaceSubclass((31, 61), 0, 32) + + pygame.display.init() + try: + surface = convert_surface.convert(depth_surface) + + self.assertIsNot(surface, depth_surface) + self.assertIsNot(surface, convert_surface) + self.assertIsInstance(surface, pygame.Surface) + self.assertIsInstance(surface, SurfaceSubclass) + self.assertEqual(surface.get_size(), expected_size) + finally: + pygame.display.quit() + + def todo_test_convert_alpha(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.convert_alpha: + + # Surface.convert_alpha(Surface): return Surface + # Surface.convert_alpha(): return Surface + # change the pixel format of an image including per pixel alphas + # + # Creates a new copy of the surface with the desired pixel format. The + # new surface will be in a format suited for quick blitting to the + # given format with per pixel alpha. If no surface is given, the new + # surface will be optimized for blitting to the current display. + # + # Unlike the Surface.convert() method, the pixel format for the new + # image will not be exactly the same as the requested source, but it + # will be optimized for fast alpha blitting to the destination. + # + + self.fail() + + def test_convert_alpha__pixel_format_as_surface_subclass(self): + """Ensure convert_alpha accepts a Surface subclass argument.""" + expected_size = (23, 17) + convert_surface = SurfaceSubclass(expected_size, SRCALPHA, 32) + depth_surface = SurfaceSubclass((31, 57), SRCALPHA, 32) + + pygame.display.init() + try: + pygame.display.set_mode((60, 60)) + + # This is accepted as an argument, but its values are ignored. + # See issue #599. + surface = convert_surface.convert_alpha(depth_surface) + + self.assertIsNot(surface, depth_surface) + self.assertIsNot(surface, convert_surface) + self.assertIsInstance(surface, pygame.Surface) + self.assertIsInstance(surface, SurfaceSubclass) + self.assertEqual(surface.get_size(), expected_size) + finally: + pygame.display.quit() + + def todo_test_get_abs_offset(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_abs_offset: + + # Surface.get_abs_offset(): return (x, y) + # find the absolute position of a child subsurface inside its top level parent + # + # Get the offset position of a child subsurface inside of its top + # level parent Surface. If the Surface is not a subsurface this will + # return (0, 0). + # + + self.fail() + + def todo_test_get_abs_parent(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_abs_parent: + + # Surface.get_abs_parent(): return Surface + # find the top level parent of a subsurface + # + # Returns the parent Surface of a subsurface. If this is not a + # subsurface then this surface will be returned. + # + + self.fail() + + def test_get_at(self): + surf = pygame.Surface((2, 2), 0, 24) + c00 = pygame.Color(1, 2, 3) + c01 = pygame.Color(5, 10, 15) + c10 = pygame.Color(100, 50, 0) + c11 = pygame.Color(4, 5, 6) + surf.set_at((0, 0), c00) + surf.set_at((0, 1), c01) + surf.set_at((1, 0), c10) + surf.set_at((1, 1), c11) + c = surf.get_at((0, 0)) + self.assertIsInstance(c, pygame.Color) + self.assertEqual(c, c00) + self.assertEqual(surf.get_at((0, 1)), c01) + self.assertEqual(surf.get_at((1, 0)), c10) + self.assertEqual(surf.get_at((1, 1)), c11) + for p in [(-1, 0), (0, -1), (2, 0), (0, 2)]: + self.assertRaises(IndexError, surf.get_at, p) + + def test_get_at_mapped(self): + color = pygame.Color(10, 20, 30) + for bitsize in [8, 16, 24, 32]: + surf = pygame.Surface((2, 2), 0, bitsize) + surf.fill(color) + pixel = surf.get_at_mapped((0, 0)) + self.assertEqual( + pixel, + surf.map_rgb(color), + "%i != %i, bitsize: %i" % (pixel, surf.map_rgb(color), bitsize), + ) + + def todo_test_get_bitsize(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_bitsize: + + # Surface.get_bitsize(): return int + # get the bit depth of the Surface pixel format + # + # Returns the number of bits used to represent each pixel. This value + # may not exactly fill the number of bytes used per pixel. For example + # a 15 bit Surface still requires a full 2 bytes. + # + + self.fail() + + def todo_test_get_clip(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_clip: + + # Surface.get_clip(): return Rect + # get the current clipping area of the Surface + # + # Return a rectangle of the current clipping area. The Surface will + # always return a valid rectangle that will never be outside the + # bounds of the image. If the Surface has had None set for the + # clipping area, the Surface will return a rectangle with the full + # area of the Surface. + # + + self.fail() + + def todo_test_get_colorkey(self): + surf = pygame.surface((2, 2), 0, 24) + self.assertIsNone(surf.get_colorykey()) + colorkey = pygame.Color(20, 40, 60) + surf.set_colorkey(colorkey) + ck = surf.get_colorkey() + self.assertIsInstance(ck, pygame.Color) + self.assertEqual(ck, colorkey) + + def todo_test_get_height(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_height: + + # Surface.get_height(): return height + # get the height of the Surface + # + # Return the height of the Surface in pixels. + + self.fail() + + def todo_test_get_locked(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_locked: + + # Surface.get_locked(): return bool + # test if the Surface is current locked + # + # Returns True when the Surface is locked. It doesn't matter how many + # times the Surface is locked. + # + + self.fail() + + def todo_test_get_locks(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_locks: + + # Surface.get_locks(): return tuple + # Gets the locks for the Surface + # + # Returns the currently existing locks for the Surface. + + self.fail() + + def todo_test_get_losses(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_losses: + + # Surface.get_losses(): return (R, G, B, A) + # the significant bits used to convert between a color and a mapped integer + # + # Return the least significant number of bits stripped from each color + # in a mapped integer. + # + # This value is not needed for normal Pygame usage. + + self.fail() + + def todo_test_get_masks(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_masks: + + # Surface.get_masks(): return (R, G, B, A) + # the bitmasks needed to convert between a color and a mapped integer + # + # Returns the bitmasks used to isolate each color in a mapped integer. + # This value is not needed for normal Pygame usage. + + self.fail() + + def todo_test_get_offset(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_offset: + + # Surface.get_offset(): return (x, y) + # find the position of a child subsurface inside a parent + # + # Get the offset position of a child subsurface inside of a parent. If + # the Surface is not a subsurface this will return (0, 0). + # + + self.fail() + + def test_get_palette(self): + pygame.display.init() + try: + palette = [Color(i, i, i) for i in range(256)] + pygame.display.set_mode((100, 50)) + surf = pygame.Surface((2, 2), 0, 8) + surf.set_palette(palette) + palette2 = surf.get_palette() + r, g, b = palette2[0] + + self.assertEqual(len(palette2), len(palette)) + for c2, c in zip(palette2, palette): + self.assertEqual(c2, c) + for c in palette2: + self.assertIsInstance(c, pygame.Color) + finally: + pygame.display.quit() + + def test_get_palette_at(self): + # See also test_get_palette + pygame.display.init() + try: + pygame.display.set_mode((100, 50)) + surf = pygame.Surface((2, 2), 0, 8) + color = pygame.Color(1, 2, 3, 255) + surf.set_palette_at(0, color) + color2 = surf.get_palette_at(0) + self.assertIsInstance(color2, pygame.Color) + self.assertEqual(color2, color) + self.assertRaises(IndexError, surf.get_palette_at, -1) + self.assertRaises(IndexError, surf.get_palette_at, 256) + finally: + pygame.display.quit() + + def todo_test_get_pitch(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_pitch: + + # Surface.get_pitch(): return int + # get the number of bytes used per Surface row + # + # Return the number of bytes separating each row in the Surface. + # Surfaces in video memory are not always linearly packed. Subsurfaces + # will also have a larger pitch than their real width. + # + # This value is not needed for normal Pygame usage. + + self.fail() + + def todo_test_get_shifts(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_shifts: + + # Surface.get_shifts(): return (R, G, B, A) + # the bit shifts needed to convert between a color and a mapped integer + # + # Returns the pixel shifts need to convert between each color and a + # mapped integer. + # + # This value is not needed for normal Pygame usage. + + self.fail() + + def todo_test_get_size(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_size: + + # Surface.get_size(): return (width, height) + # get the dimensions of the Surface + # + # Return the width and height of the Surface in pixels. + + self.fail() + + def todo_test_lock(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.lock: + + # Surface.lock(): return None + # lock the Surface memory for pixel access + # + # Lock the pixel data of a Surface for access. On accelerated + # Surfaces, the pixel data may be stored in volatile video memory or + # nonlinear compressed forms. When a Surface is locked the pixel + # memory becomes available to access by regular software. Code that + # reads or writes pixel values will need the Surface to be locked. + # + # Surfaces should not remain locked for more than necessary. A locked + # Surface can often not be displayed or managed by Pygame. + # + # Not all Surfaces require locking. The Surface.mustlock() method can + # determine if it is actually required. There is no performance + # penalty for locking and unlocking a Surface that does not need it. + # + # All pygame functions will automatically lock and unlock the Surface + # data as needed. If a section of code is going to make calls that + # will repeatedly lock and unlock the Surface many times, it can be + # helpful to wrap the block inside a lock and unlock pair. + # + # It is safe to nest locking and unlocking calls. The surface will + # only be unlocked after the final lock is released. + # + + self.fail() + + def test_map_rgb(self): + color = Color(0, 128, 255, 64) + surf = pygame.Surface((5, 5), SRCALPHA, 32) + c = surf.map_rgb(color) + self.assertEqual(surf.unmap_rgb(c), color) + + self.assertEqual(surf.get_at((0, 0)), (0, 0, 0, 0)) + surf.fill(c) + self.assertEqual(surf.get_at((0, 0)), color) + + surf.fill((0, 0, 0, 0)) + self.assertEqual(surf.get_at((0, 0)), (0, 0, 0, 0)) + surf.set_at((0, 0), c) + self.assertEqual(surf.get_at((0, 0)), color) + + def todo_test_mustlock(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.mustlock: + + # Surface.mustlock(): return bool + # test if the Surface requires locking + # + # Returns True if the Surface is required to be locked to access pixel + # data. Usually pure software Surfaces do not require locking. This + # method is rarely needed, since it is safe and quickest to just lock + # all Surfaces as needed. + # + # All pygame functions will automatically lock and unlock the Surface + # data as needed. If a section of code is going to make calls that + # will repeatedly lock and unlock the Surface many times, it can be + # helpful to wrap the block inside a lock and unlock pair. + # + + self.fail() + + def test_set_alpha_none(self): + """surf.set_alpha(None) disables blending""" + s = pygame.Surface((1, 1), SRCALPHA, 32) + s.fill((0, 255, 0, 128)) + s.set_alpha(None) + self.assertEqual(None, s.get_alpha()) + + s2 = pygame.Surface((1, 1), SRCALPHA, 32) + s2.fill((255, 0, 0, 255)) + s2.blit(s, (0, 0)) + self.assertEqual(s2.get_at((0, 0))[0], 0, "the red component should be 0") + + def test_set_alpha_value(self): + """surf.set_alpha(x), where x != None, enables blending""" + s = pygame.Surface((1, 1), SRCALPHA, 32) + s.fill((0, 255, 0, 128)) + s.set_alpha(255) + + s2 = pygame.Surface((1, 1), SRCALPHA, 32) + s2.fill((255, 0, 0, 255)) + s2.blit(s, (0, 0)) + self.assertGreater( + s2.get_at((0, 0))[0], 0, "the red component should be above 0" + ) + + def test_palette_colorkey(self): + """ test bug discovered by robertpfeiffer + https://github.com/pygame/pygame/issues/721 + """ + surf = pygame.image.load(example_path(os.path.join("data", "alien2.png"))) + key = surf.get_colorkey() + self.assertEqual(surf.get_palette()[surf.map_rgb(key)], key) + + def test_palette_colorkey_set_px(self): + surf = pygame.image.load(example_path(os.path.join("data", "alien2.png"))) + key = surf.get_colorkey() + surf.set_at((0, 0), key) + self.assertEqual(surf.get_at((0, 0)), key) + + def test_palette_colorkey_fill(self): + surf = pygame.image.load(example_path(os.path.join("data", "alien2.png"))) + key = surf.get_colorkey() + surf.fill(key) + self.assertEqual(surf.get_at((0, 0)), key) + + def test_set_palette(self): + palette = [pygame.Color(i, i, i) for i in range(256)] + palette[10] = tuple(palette[10]) # 4 element tuple + palette[11] = tuple(palette[11])[0:3] # 3 element tuple + + surf = pygame.Surface((2, 2), 0, 8) + pygame.display.init() + try: + pygame.display.set_mode((100, 50)) + surf.set_palette(palette) + for i in range(256): + self.assertEqual(surf.map_rgb(palette[i]), i, "palette color %i" % (i,)) + c = palette[i] + surf.fill(c) + self.assertEqual(surf.get_at((0, 0)), c, "palette color %i" % (i,)) + for i in range(10): + palette[i] = pygame.Color(255 - i, 0, 0) + surf.set_palette(palette[0:10]) + for i in range(256): + self.assertEqual(surf.map_rgb(palette[i]), i, "palette color %i" % (i,)) + c = palette[i] + surf.fill(c) + self.assertEqual(surf.get_at((0, 0)), c, "palette color %i" % (i,)) + self.assertRaises(ValueError, surf.set_palette, [Color(1, 2, 3, 254)]) + self.assertRaises(ValueError, surf.set_palette, (1, 2, 3, 254)) + finally: + pygame.display.quit() + + def test_set_palette__fail(self): + pygame.init() + palette = 256 * [(10, 20, 30)] + surf = pygame.Surface((2, 2), 0, 32) + self.assertRaises(pygame.error, surf.set_palette, palette) + pygame.quit() + + def test_set_palette_at(self): + pygame.display.init() + try: + pygame.display.set_mode((100, 50)) + surf = pygame.Surface((2, 2), 0, 8) + original = surf.get_palette_at(10) + replacement = Color(1, 1, 1, 255) + if replacement == original: + replacement = Color(2, 2, 2, 255) + surf.set_palette_at(10, replacement) + self.assertEqual(surf.get_palette_at(10), replacement) + next = tuple(original) + surf.set_palette_at(10, next) + self.assertEqual(surf.get_palette_at(10), next) + next = tuple(original)[0:3] + surf.set_palette_at(10, next) + self.assertEqual(surf.get_palette_at(10), next) + self.assertRaises(IndexError, surf.set_palette_at, 256, replacement) + self.assertRaises(IndexError, surf.set_palette_at, -1, replacement) + finally: + pygame.display.quit() + + def test_subsurface(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.subsurface: + + # Surface.subsurface(Rect): return Surface + # create a new surface that references its parent + # + # Returns a new Surface that shares its pixels with its new parent. + # The new Surface is considered a child of the original. Modifications + # to either Surface pixels will effect each other. Surface information + # like clipping area and color keys are unique to each Surface. + # + # The new Surface will inherit the palette, color key, and alpha + # settings from its parent. + # + # It is possible to have any number of subsurfaces and subsubsurfaces + # on the parent. It is also possible to subsurface the display Surface + # if the display mode is not hardware accelerated. + # + # See the Surface.get_offset(), Surface.get_parent() to learn more + # about the state of a subsurface. + # + + surf = pygame.Surface((16, 16)) + s = surf.subsurface(0, 0, 1, 1) + s = surf.subsurface((0, 0, 1, 1)) + + # s = surf.subsurface((0,0,1,1), 1) + # This form is not acceptable. + # s = surf.subsurface(0,0,10,10, 1) + + self.assertRaises(ValueError, surf.subsurface, (0, 0, 1, 1, 666)) + + self.assertEqual(s.get_shifts(), surf.get_shifts()) + self.assertEqual(s.get_masks(), surf.get_masks()) + self.assertEqual(s.get_losses(), surf.get_losses()) + + # Issue 2 at Bitbucket.org/pygame/pygame + surf = pygame.Surface.__new__(pygame.Surface) + self.assertRaises(pygame.error, surf.subsurface, (0, 0, 0, 0)) + + def todo_test_unlock(self): + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.unlock: + + # Surface.unlock(): return None + # unlock the Surface memory from pixel access + # + # Unlock the Surface pixel data after it has been locked. The unlocked + # Surface can once again be drawn and managed by Pygame. See the + # Surface.lock() documentation for more details. + # + # All pygame functions will automatically lock and unlock the Surface + # data as needed. If a section of code is going to make calls that + # will repeatedly lock and unlock the Surface many times, it can be + # helpful to wrap the block inside a lock and unlock pair. + # + # It is safe to nest locking and unlocking calls. The surface will + # only be unlocked after the final lock is released. + # + + self.fail() + + def test_unmap_rgb(self): + # Special case, 8 bit-per-pixel surface (has a palette). + surf = pygame.Surface((2, 2), 0, 8) + c = (1, 1, 1) # Unlikely to be in a default palette. + i = 67 + pygame.display.init() + try: + pygame.display.set_mode((100, 50)) + surf.set_palette_at(i, c) + unmapped_c = surf.unmap_rgb(i) + self.assertEqual(unmapped_c, c) + # Confirm it is a Color instance + self.assertIsInstance(unmapped_c, pygame.Color) + finally: + pygame.display.quit() + + # Remaining, non-pallete, cases. + c = (128, 64, 12, 255) + formats = [(0, 16), (0, 24), (0, 32), (SRCALPHA, 16), (SRCALPHA, 32)] + for flags, bitsize in formats: + surf = pygame.Surface((2, 2), flags, bitsize) + unmapped_c = surf.unmap_rgb(surf.map_rgb(c)) + surf.fill(c) + comparison_c = surf.get_at((0, 0)) + self.assertEqual( + unmapped_c, + comparison_c, + "%s != %s, flags: %i, bitsize: %i" + % (unmapped_c, comparison_c, flags, bitsize), + ) + # Confirm it is a Color instance + self.assertIsInstance(unmapped_c, pygame.Color) + + def test_scroll(self): + scrolls = [ + (8, 2, 3), + (16, 2, 3), + (24, 2, 3), + (32, 2, 3), + (32, -1, -3), + (32, 0, 0), + (32, 11, 0), + (32, 0, 11), + (32, -11, 0), + (32, 0, -11), + (32, -11, 2), + (32, 2, -11), + ] + for bitsize, dx, dy in scrolls: + surf = pygame.Surface((10, 10), 0, bitsize) + surf.fill((255, 0, 0)) + surf.fill((0, 255, 0), (2, 2, 2, 2)) + comp = surf.copy() + comp.blit(surf, (dx, dy)) + surf.scroll(dx, dy) + w, h = surf.get_size() + for x in range(w): + for y in range(h): + self.assertEqual( + surf.get_at((x, y)), + comp.get_at((x, y)), + "%s != %s, bpp:, %i, x: %i, y: %i" + % (surf.get_at((x, y)), comp.get_at((x, y)), bitsize, dx, dy), + ) + # Confirm clip rect containment + surf = pygame.Surface((20, 13), 0, 32) + surf.fill((255, 0, 0)) + surf.fill((0, 255, 0), (7, 1, 6, 6)) + comp = surf.copy() + clip = Rect(3, 1, 8, 14) + surf.set_clip(clip) + comp.set_clip(clip) + comp.blit(surf, (clip.x + 2, clip.y + 3), surf.get_clip()) + surf.scroll(2, 3) + w, h = surf.get_size() + for x in range(w): + for y in range(h): + self.assertEqual(surf.get_at((x, y)), comp.get_at((x, y))) + # Confirm keyword arguments and per-pixel alpha + spot_color = (0, 255, 0, 128) + surf = pygame.Surface((4, 4), pygame.SRCALPHA, 32) + surf.fill((255, 0, 0, 255)) + surf.set_at((1, 1), spot_color) + surf.scroll(dx=1) + self.assertEqual(surf.get_at((2, 1)), spot_color) + surf.scroll(dy=1) + self.assertEqual(surf.get_at((2, 2)), spot_color) + surf.scroll(dy=1, dx=1) + self.assertEqual(surf.get_at((3, 3)), spot_color) + surf.scroll(dx=-3, dy=-3) + self.assertEqual(surf.get_at((0, 0)), spot_color) + + +class SurfaceSubtypeTest(unittest.TestCase): + """Issue #280: Methods that return a new Surface preserve subclasses""" + + def setUp(self): + pygame.display.init() + + def tearDown(self): + pygame.display.quit() + + def test_copy(self): + """Ensure method copy() preserves the surface's class + + When Surface is subclassed, the inherited copy() method will return + instances of the subclass. Non Surface fields are uncopied, however. + This includes instance attributes. + """ + expected_size = (32, 32) + ms1 = SurfaceSubclass(expected_size, SRCALPHA, 32) + ms2 = ms1.copy() + + self.assertIsNot(ms1, ms2) + self.assertIsInstance(ms1, pygame.Surface) + self.assertIsInstance(ms2, pygame.Surface) + self.assertIsInstance(ms1, SurfaceSubclass) + self.assertIsInstance(ms2, SurfaceSubclass) + self.assertTrue(ms1.test_attribute) + self.assertRaises(AttributeError, getattr, ms2, "test_attribute") + self.assertEqual(ms2.get_size(), expected_size) + + def test_convert(self): + """Ensure method convert() preserves the surface's class + + When Surface is subclassed, the inherited convert() method will return + instances of the subclass. Non Surface fields are omitted, however. + This includes instance attributes. + """ + expected_size = (32, 32) + ms1 = SurfaceSubclass(expected_size, 0, 24) + ms2 = ms1.convert(24) + + self.assertIsNot(ms1, ms2) + self.assertIsInstance(ms1, pygame.Surface) + self.assertIsInstance(ms2, pygame.Surface) + self.assertIsInstance(ms1, SurfaceSubclass) + self.assertIsInstance(ms2, SurfaceSubclass) + self.assertTrue(ms1.test_attribute) + self.assertRaises(AttributeError, getattr, ms2, "test_attribute") + self.assertEqual(ms2.get_size(), expected_size) + + def test_convert_alpha(self): + """Ensure method convert_alpha() preserves the surface's class + + When Surface is subclassed, the inherited convert_alpha() method will + return instances of the subclass. Non Surface fields are omitted, + however. This includes instance attributes. + """ + pygame.display.set_mode((40, 40)) + expected_size = (32, 32) + s = pygame.Surface(expected_size, SRCALPHA, 16) + ms1 = SurfaceSubclass(expected_size, SRCALPHA, 32) + ms2 = ms1.convert_alpha(s) + + self.assertIsNot(ms1, ms2) + self.assertIsInstance(ms1, pygame.Surface) + self.assertIsInstance(ms2, pygame.Surface) + self.assertIsInstance(ms1, SurfaceSubclass) + self.assertIsInstance(ms2, SurfaceSubclass) + self.assertTrue(ms1.test_attribute) + self.assertRaises(AttributeError, getattr, ms2, "test_attribute") + self.assertEqual(ms2.get_size(), expected_size) + + def test_subsurface(self): + """Ensure method subsurface() preserves the surface's class + + When Surface is subclassed, the inherited subsurface() method will + return instances of the subclass. Non Surface fields are uncopied, + however. This includes instance attributes. + """ + expected_size = (10, 12) + ms1 = SurfaceSubclass((32, 32), SRCALPHA, 32) + ms2 = ms1.subsurface((4, 5), expected_size) + + self.assertIsNot(ms1, ms2) + self.assertIsInstance(ms1, pygame.Surface) + self.assertIsInstance(ms2, pygame.Surface) + self.assertIsInstance(ms1, SurfaceSubclass) + self.assertIsInstance(ms2, SurfaceSubclass) + self.assertTrue(ms1.test_attribute) + self.assertRaises(AttributeError, getattr, ms2, "test_attribute") + self.assertEqual(ms2.get_size(), expected_size) + + +class SurfaceGetBufferTest(unittest.TestCase): + + # These tests requires ctypes. They are disabled if ctypes + # is not installed. + # + try: + ArrayInterface + except NameError: + __tags__ = ("ignore", "subprocess_ignore") + + lilendian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN + + def _check_interface_2D(self, s): + s_w, s_h = s.get_size() + s_bytesize = s.get_bytesize() + s_pitch = s.get_pitch() + s_pixels = s._pixels_address + + # check the array interface structure fields. + v = s.get_view("2") + if not IS_PYPY: + flags = PAI_ALIGNED | PAI_NOTSWAPPED | PAI_WRITEABLE + if s.get_pitch() == s_w * s_bytesize: + flags |= PAI_FORTRAN + + inter = ArrayInterface(v) + + self.assertEqual(inter.two, 2) + self.assertEqual(inter.nd, 2) + self.assertEqual(inter.typekind, "u") + self.assertEqual(inter.itemsize, s_bytesize) + self.assertEqual(inter.shape[0], s_w) + self.assertEqual(inter.shape[1], s_h) + self.assertEqual(inter.strides[0], s_bytesize) + self.assertEqual(inter.strides[1], s_pitch) + self.assertEqual(inter.flags, flags) + self.assertEqual(inter.data, s_pixels) + + def _check_interface_3D(self, s): + s_w, s_h = s.get_size() + s_bytesize = s.get_bytesize() + s_pitch = s.get_pitch() + s_pixels = s._pixels_address + s_shifts = list(s.get_shifts()) + + # Check for RGB or BGR surface. + if s_shifts[0:3] == [0, 8, 16]: + if self.lilendian: + # RGB + offset = 0 + step = 1 + else: + # BGR + offset = s_bytesize - 1 + step = -1 + elif s_shifts[0:3] == [8, 16, 24]: + if self.lilendian: + # xRGB + offset = 1 + step = 1 + else: + # BGRx + offset = s_bytesize - 2 + step = -1 + elif s_shifts[0:3] == [16, 8, 0]: + if self.lilendian: + # BGR + offset = 2 + step = -1 + else: + # RGB + offset = s_bytesize - 3 + step = 1 + elif s_shifts[0:3] == [24, 16, 8]: + if self.lilendian: + # BGRx + offset = 2 + step = -1 + else: + # RGBx + offset = s_bytesize - 4 + step = -1 + else: + return + + # check the array interface structure fields. + v = s.get_view("3") + if not IS_PYPY: + inter = ArrayInterface(v) + flags = PAI_ALIGNED | PAI_NOTSWAPPED | PAI_WRITEABLE + self.assertEqual(inter.two, 2) + self.assertEqual(inter.nd, 3) + self.assertEqual(inter.typekind, "u") + self.assertEqual(inter.itemsize, 1) + self.assertEqual(inter.shape[0], s_w) + self.assertEqual(inter.shape[1], s_h) + self.assertEqual(inter.shape[2], 3) + self.assertEqual(inter.strides[0], s_bytesize) + self.assertEqual(inter.strides[1], s_pitch) + self.assertEqual(inter.strides[2], step) + self.assertEqual(inter.flags, flags) + self.assertEqual(inter.data, s_pixels + offset) + + def _check_interface_rgba(self, s, plane): + s_w, s_h = s.get_size() + s_bytesize = s.get_bytesize() + s_pitch = s.get_pitch() + s_pixels = s._pixels_address + s_shifts = s.get_shifts() + s_masks = s.get_masks() + + # Find the color plane position within the pixel. + if not s_masks[plane]: + return + alpha_shift = s_shifts[plane] + offset = alpha_shift // 8 + if not self.lilendian: + offset = s_bytesize - offset - 1 + + # check the array interface structure fields. + v = s.get_view("rgba"[plane]) + if not IS_PYPY: + inter = ArrayInterface(v) + flags = PAI_ALIGNED | PAI_NOTSWAPPED | PAI_WRITEABLE + self.assertEqual(inter.two, 2) + self.assertEqual(inter.nd, 2) + self.assertEqual(inter.typekind, "u") + self.assertEqual(inter.itemsize, 1) + self.assertEqual(inter.shape[0], s_w) + self.assertEqual(inter.shape[1], s_h) + self.assertEqual(inter.strides[0], s_bytesize) + self.assertEqual(inter.strides[1], s_pitch) + self.assertEqual(inter.flags, flags) + self.assertEqual(inter.data, s_pixels + offset) + + def test_array_interface(self): + self._check_interface_2D(pygame.Surface((5, 7), 0, 8)) + self._check_interface_2D(pygame.Surface((5, 7), 0, 16)) + self._check_interface_2D(pygame.Surface((5, 7), pygame.SRCALPHA, 16)) + self._check_interface_3D(pygame.Surface((5, 7), 0, 24)) + self._check_interface_3D(pygame.Surface((8, 4), 0, 24)) # No gaps + self._check_interface_2D(pygame.Surface((5, 7), 0, 32)) + self._check_interface_3D(pygame.Surface((5, 7), 0, 32)) + self._check_interface_2D(pygame.Surface((5, 7), pygame.SRCALPHA, 32)) + self._check_interface_3D(pygame.Surface((5, 7), pygame.SRCALPHA, 32)) + + def test_array_interface_masks(self): + """Test non-default color byte orders on 3D views""" + + sz = (5, 7) + # Reversed RGB byte order + s = pygame.Surface(sz, 0, 32) + s_masks = list(s.get_masks()) + masks = [0xFF, 0xFF00, 0xFF0000] + if s_masks[0:3] == masks or s_masks[0:3] == masks[::-1]: + masks = s_masks[2::-1] + s_masks[3:4] + self._check_interface_3D(pygame.Surface(sz, 0, 32, masks)) + s = pygame.Surface(sz, 0, 24) + s_masks = list(s.get_masks()) + masks = [0xFF, 0xFF00, 0xFF0000] + if s_masks[0:3] == masks or s_masks[0:3] == masks[::-1]: + masks = s_masks[2::-1] + s_masks[3:4] + self._check_interface_3D(pygame.Surface(sz, 0, 24, masks)) + + masks = [0xFF00, 0xFF0000, 0xFF000000, 0] + self._check_interface_3D(pygame.Surface(sz, 0, 32, masks)) + + # Unsupported RGB byte orders + if pygame.get_sdl_version()[0] == 1: + # Invalid mask values with SDL2 + masks = [0xFF00, 0xFF, 0xFF0000, 0] + self.assertRaises( + ValueError, pygame.Surface(sz, 0, 24, masks).get_view, "3" + ) + + def test_array_interface_alpha(self): + for shifts in [[0, 8, 16, 24], [8, 16, 24, 0], [24, 16, 8, 0], [16, 8, 0, 24]]: + masks = [0xFF << s for s in shifts] + s = pygame.Surface((4, 2), pygame.SRCALPHA, 32, masks) + self._check_interface_rgba(s, 3) + + def test_array_interface_rgb(self): + for shifts in [[0, 8, 16, 24], [8, 16, 24, 0], [24, 16, 8, 0], [16, 8, 0, 24]]: + masks = [0xFF << s for s in shifts] + masks[3] = 0 + for plane in range(3): + s = pygame.Surface((4, 2), 0, 24) + self._check_interface_rgba(s, plane) + s = pygame.Surface((4, 2), 0, 32) + self._check_interface_rgba(s, plane) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_newbuf_PyBUF_flags_bytes(self): + from pygame.tests.test_utils import buftools + + Importer = buftools.Importer + s = pygame.Surface((10, 6), 0, 32) + a = s.get_buffer() + b = Importer(a, buftools.PyBUF_SIMPLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 1) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address) + b = Importer(a, buftools.PyBUF_WRITABLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertFalse(b.readonly) + b = Importer(a, buftools.PyBUF_FORMAT) + self.assertEqual(b.ndim, 0) + self.assertEqual(b.format, "B") + b = Importer(a, buftools.PyBUF_ND) + self.assertEqual(b.ndim, 1) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 1) + self.assertEqual(b.shape, (a.length,)) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address) + b = Importer(a, buftools.PyBUF_STRIDES) + self.assertEqual(b.ndim, 1) + self.assertTrue(b.format is None) + self.assertEqual(b.strides, (1,)) + s2 = s.subsurface((1, 1, 7, 4)) # Not contiguous + a = s2.get_buffer() + b = Importer(a, buftools.PyBUF_SIMPLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 1) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s2._pixels_address) + b = Importer(a, buftools.PyBUF_C_CONTIGUOUS) + self.assertEqual(b.ndim, 1) + self.assertEqual(b.strides, (1,)) + b = Importer(a, buftools.PyBUF_F_CONTIGUOUS) + self.assertEqual(b.ndim, 1) + self.assertEqual(b.strides, (1,)) + b = Importer(a, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertEqual(b.ndim, 1) + self.assertEqual(b.strides, (1,)) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_newbuf_PyBUF_flags_0D(self): + # This is the same handler as used by get_buffer(), so just + # confirm that it succeeds for one case. + from pygame.tests.test_utils import buftools + + Importer = buftools.Importer + s = pygame.Surface((10, 6), 0, 32) + a = s.get_view("0") + b = Importer(a, buftools.PyBUF_SIMPLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 1) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_newbuf_PyBUF_flags_1D(self): + from pygame.tests.test_utils import buftools + + Importer = buftools.Importer + s = pygame.Surface((10, 6), 0, 32) + a = s.get_view("1") + b = Importer(a, buftools.PyBUF_SIMPLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, s.get_bytesize()) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address) + b = Importer(a, buftools.PyBUF_WRITABLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertFalse(b.readonly) + b = Importer(a, buftools.PyBUF_FORMAT) + self.assertEqual(b.ndim, 0) + self.assertEqual(b.format, "=I") + b = Importer(a, buftools.PyBUF_ND) + self.assertEqual(b.ndim, 1) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, s.get_bytesize()) + self.assertEqual(b.shape, (s.get_width() * s.get_height(),)) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address) + b = Importer(a, buftools.PyBUF_STRIDES) + self.assertEqual(b.ndim, 1) + self.assertTrue(b.format is None) + self.assertEqual(b.strides, (s.get_bytesize(),)) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_newbuf_PyBUF_flags_2D(self): + from pygame.tests.test_utils import buftools + + Importer = buftools.Importer + s = pygame.Surface((10, 6), 0, 32) + a = s.get_view("2") + # Non dimensional requests, no PyDEF_ND, are handled by the + # 1D surface buffer code, so only need to confirm a success. + b = Importer(a, buftools.PyBUF_SIMPLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, s.get_bytesize()) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address) + # Uniquely 2D + b = Importer(a, buftools.PyBUF_STRIDES) + self.assertEqual(b.ndim, 2) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, s.get_bytesize()) + self.assertEqual(b.shape, s.get_size()) + self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch())) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address) + b = Importer(a, buftools.PyBUF_RECORDS_RO) + self.assertEqual(b.ndim, 2) + self.assertEqual(b.format, "=I") + self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch())) + b = Importer(a, buftools.PyBUF_RECORDS) + self.assertEqual(b.ndim, 2) + self.assertEqual(b.format, "=I") + self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch())) + b = Importer(a, buftools.PyBUF_F_CONTIGUOUS) + self.assertEqual(b.ndim, 2) + self.assertEqual(b.format, None) + self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch())) + b = Importer(a, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertEqual(b.ndim, 2) + self.assertEqual(b.format, None) + self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch())) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + s2 = s.subsurface((1, 1, 7, 4)) # Not contiguous + a = s2.get_view("2") + b = Importer(a, buftools.PyBUF_STRIDES) + self.assertEqual(b.ndim, 2) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, s2.get_bytesize()) + self.assertEqual(b.shape, s2.get_size()) + self.assertEqual(b.strides, (s2.get_bytesize(), s.get_pitch())) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s2._pixels_address) + b = Importer(a, buftools.PyBUF_RECORDS) + self.assertEqual(b.ndim, 2) + self.assertEqual(b.format, "=I") + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FORMAT) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_newbuf_PyBUF_flags_3D(self): + from pygame.tests.test_utils import buftools + + Importer = buftools.Importer + s = pygame.Surface((12, 6), 0, 24) + rmask, gmask, bmask, amask = s.get_masks() + if self.lilendian: + if rmask == 0x0000FF: + color_step = 1 + addr_offset = 0 + else: + color_step = -1 + addr_offset = 2 + else: + if rmask == 0xFF0000: + color_step = 1 + addr_offset = 0 + else: + color_step = -1 + addr_offset = 2 + a = s.get_view("3") + b = Importer(a, buftools.PyBUF_STRIDES) + w, h = s.get_size() + shape = w, h, 3 + strides = 3, s.get_pitch(), color_step + self.assertEqual(b.ndim, 3) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 1) + self.assertEqual(b.shape, shape) + self.assertEqual(b.strides, strides) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address + addr_offset) + b = Importer(a, buftools.PyBUF_RECORDS_RO) + self.assertEqual(b.ndim, 3) + self.assertEqual(b.format, "B") + self.assertEqual(b.strides, strides) + b = Importer(a, buftools.PyBUF_RECORDS) + self.assertEqual(b.ndim, 3) + self.assertEqual(b.format, "B") + self.assertEqual(b.strides, strides) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FORMAT) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_newbuf_PyBUF_flags_rgba(self): + # All color plane views are handled by the same routine, + # so only one plane need be checked. + from pygame.tests.test_utils import buftools + + Importer = buftools.Importer + s = pygame.Surface((12, 6), 0, 24) + rmask, gmask, bmask, amask = s.get_masks() + if self.lilendian: + if rmask == 0x0000FF: + addr_offset = 0 + else: + addr_offset = 2 + else: + if rmask == 0xFF0000: + addr_offset = 0 + else: + addr_offset = 2 + a = s.get_view("R") + b = Importer(a, buftools.PyBUF_STRIDES) + w, h = s.get_size() + shape = w, h + strides = s.get_bytesize(), s.get_pitch() + self.assertEqual(b.ndim, 2) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 1) + self.assertEqual(b.shape, shape) + self.assertEqual(b.strides, strides) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address + addr_offset) + b = Importer(a, buftools.PyBUF_RECORDS_RO) + self.assertEqual(b.ndim, 2) + self.assertEqual(b.format, "B") + self.assertEqual(b.strides, strides) + b = Importer(a, buftools.PyBUF_RECORDS) + self.assertEqual(b.ndim, 2) + self.assertEqual(b.format, "B") + self.assertEqual(b.strides, strides) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FORMAT) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) + + +class SurfaceBlendTest(unittest.TestCase): + def setUp(self): + # Needed for 8 bits-per-pixel color palette surface tests. + pygame.display.init() + + def tearDown(self): + pygame.display.quit() + + _test_palette = [ + (0, 0, 0, 255), + (10, 30, 60, 0), + (25, 75, 100, 128), + (200, 150, 100, 200), + (0, 100, 200, 255), + ] + surf_size = (10, 12) + _test_points = [ + ((0, 0), 1), + ((4, 5), 1), + ((9, 0), 2), + ((5, 5), 2), + ((0, 11), 3), + ((4, 6), 3), + ((9, 11), 4), + ((5, 6), 4), + ] + + def _make_surface(self, bitsize, srcalpha=False, palette=None): + if palette is None: + palette = self._test_palette + flags = 0 + if srcalpha: + flags |= SRCALPHA + surf = pygame.Surface(self.surf_size, flags, bitsize) + if bitsize == 8: + surf.set_palette([c[:3] for c in palette]) + return surf + + def _fill_surface(self, surf, palette=None): + if palette is None: + palette = self._test_palette + surf.fill(palette[1], (0, 0, 5, 6)) + surf.fill(palette[2], (5, 0, 5, 6)) + surf.fill(palette[3], (0, 6, 5, 6)) + surf.fill(palette[4], (5, 6, 5, 6)) + + def _make_src_surface(self, bitsize, srcalpha=False, palette=None): + surf = self._make_surface(bitsize, srcalpha, palette) + self._fill_surface(surf, palette) + return surf + + def _assert_surface(self, surf, palette=None, msg=""): + if palette is None: + palette = self._test_palette + if surf.get_bitsize() == 16: + palette = [surf.unmap_rgb(surf.map_rgb(c)) for c in palette] + for posn, i in self._test_points: + self.assertEqual( + surf.get_at(posn), + palette[i], + "%s != %s: flags: %i, bpp: %i, posn: %s%s" + % ( + surf.get_at(posn), + palette[i], + surf.get_flags(), + surf.get_bitsize(), + posn, + msg, + ), + ) + + def test_blit_blend(self): + sources = [ + self._make_src_surface(8), + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] + destinations = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + blend = [ + ("BLEND_ADD", (0, 25, 100, 255), lambda a, b: min(a + b, 255)), + ("BLEND_SUB", (100, 25, 0, 100), lambda a, b: max(a - b, 0)), + ("BLEND_MULT", (100, 200, 0, 0), lambda a, b: (a * b) // 256), + ("BLEND_MIN", (255, 0, 0, 255), min), + ("BLEND_MAX", (0, 255, 0, 255), max), + ] + + for src in sources: + src_palette = [src.unmap_rgb(src.map_rgb(c)) for c in self._test_palette] + for dst in destinations: + for blend_name, dst_color, op in blend: + dc = dst.unmap_rgb(dst.map_rgb(dst_color)) + p = [] + for sc in src_palette: + c = [op(dc[i], sc[i]) for i in range(3)] + if dst.get_masks()[3]: + c.append(dc[3]) + else: + c.append(255) + c = dst.unmap_rgb(dst.map_rgb(c)) + p.append(c) + dst.fill(dst_color) + dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name)) + self._assert_surface( + dst, + p, + ( + ", op: %s, src bpp: %i" + ", src flags: %i" + % (blend_name, src.get_bitsize(), src.get_flags()) + ), + ) + + src = self._make_src_surface(32) + masks = src.get_masks() + dst = pygame.Surface( + src.get_size(), 0, 32, [masks[2], masks[1], masks[0], masks[3]] + ) + for blend_name, dst_color, op in blend: + p = [] + for src_color in self._test_palette: + c = [op(dst_color[i], src_color[i]) for i in range(3)] + c.append(255) + p.append(tuple(c)) + dst.fill(dst_color) + dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name)) + self._assert_surface(dst, p, ", %s" % blend_name) + + # Blend blits are special cased for 32 to 32 bit surfaces. + # + # Confirm that it works when the rgb bytes are not the + # least significant bytes. + pat = self._make_src_surface(32) + masks = pat.get_masks() + if min(masks) == intify(0xFF000000): + masks = [longify(m) >> 8 for m in masks] + else: + masks = [intify(m << 8) for m in masks] + src = pygame.Surface(pat.get_size(), 0, 32, masks) + self._fill_surface(src) + dst = pygame.Surface(src.get_size(), 0, 32, masks) + for blend_name, dst_color, op in blend: + p = [] + for src_color in self._test_palette: + c = [op(dst_color[i], src_color[i]) for i in range(3)] + c.append(255) + p.append(tuple(c)) + dst.fill(dst_color) + dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name)) + self._assert_surface(dst, p, ", %s" % blend_name) + + def test_blit_blend_rgba(self): + sources = [ + self._make_src_surface(8), + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] + destinations = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + blend = [ + ("BLEND_RGBA_ADD", (0, 25, 100, 255), lambda a, b: min(a + b, 255)), + ("BLEND_RGBA_SUB", (0, 25, 100, 255), lambda a, b: max(a - b, 0)), + ("BLEND_RGBA_MULT", (0, 7, 100, 255), lambda a, b: (a * b) // 256), + ("BLEND_RGBA_MIN", (0, 255, 0, 255), min), + ("BLEND_RGBA_MAX", (0, 255, 0, 255), max), + ] + + for src in sources: + src_palette = [src.unmap_rgb(src.map_rgb(c)) for c in self._test_palette] + for dst in destinations: + for blend_name, dst_color, op in blend: + dc = dst.unmap_rgb(dst.map_rgb(dst_color)) + p = [] + for sc in src_palette: + c = [op(dc[i], sc[i]) for i in range(4)] + if not dst.get_masks()[3]: + c[3] = 255 + c = dst.unmap_rgb(dst.map_rgb(c)) + p.append(c) + dst.fill(dst_color) + dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name)) + self._assert_surface( + dst, + p, + ( + ", op: %s, src bpp: %i" + ", src flags: %i" + % (blend_name, src.get_bitsize(), src.get_flags()) + ), + ) + + # Blend blits are special cased for 32 to 32 bit surfaces + # with per-pixel alpha. + # + # Confirm the general case is used instead when the formats differ. + src = self._make_src_surface(32, srcalpha=True) + masks = src.get_masks() + dst = pygame.Surface( + src.get_size(), SRCALPHA, 32, (masks[2], masks[1], masks[0], masks[3]) + ) + for blend_name, dst_color, op in blend: + p = [ + tuple([op(dst_color[i], src_color[i]) for i in range(4)]) + for src_color in self._test_palette + ] + dst.fill(dst_color) + dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name)) + self._assert_surface(dst, p, ", %s" % blend_name) + + # Confirm this special case handles subsurfaces. + src = pygame.Surface((8, 10), SRCALPHA, 32) + dst = pygame.Surface((8, 10), SRCALPHA, 32) + tst = pygame.Surface((8, 10), SRCALPHA, 32) + src.fill((1, 2, 3, 4)) + dst.fill((40, 30, 20, 10)) + subsrc = src.subsurface((2, 3, 4, 4)) + subdst = dst.subsurface((2, 3, 4, 4)) + subdst.blit(subsrc, (0, 0), special_flags=BLEND_RGBA_ADD) + tst.fill((40, 30, 20, 10)) + tst.fill((41, 32, 23, 14), (2, 3, 4, 4)) + for x in range(8): + for y in range(10): + self.assertEqual( + dst.get_at((x, y)), + tst.get_at((x, y)), + "%s != %s at (%i, %i)" + % (dst.get_at((x, y)), tst.get_at((x, y)), x, y), + ) + + def test_blit_blend_big_rect(self): + """ test that an oversized rect works ok. + """ + color = (1, 2, 3, 255) + area = (1, 1, 30, 30) + s1 = pygame.Surface((4, 4), 0, 32) + r = s1.fill(special_flags=pygame.BLEND_ADD, color=color, rect=area) + + self.assertEqual(pygame.Rect((1, 1, 3, 3)), r) + self.assertEqual(s1.get_at((0, 0)), (0, 0, 0, 255)) + self.assertEqual(s1.get_at((1, 1)), color) + + black = pygame.Color("black") + red = pygame.Color("red") + self.assertNotEqual(black, red) + + surf = pygame.Surface((10, 10), 0, 32) + surf.fill(black) + subsurf = surf.subsurface(pygame.Rect(0, 1, 10, 8)) + self.assertEqual(surf.get_at((0, 0)), black) + self.assertEqual(surf.get_at((0, 9)), black) + + subsurf.fill(red, (0, -1, 10, 1), pygame.BLEND_RGB_ADD) + self.assertEqual(surf.get_at((0, 0)), black) + self.assertEqual(surf.get_at((0, 9)), black) + + subsurf.fill(red, (0, 8, 10, 1), pygame.BLEND_RGB_ADD) + self.assertEqual(surf.get_at((0, 0)), black) + self.assertEqual(surf.get_at((0, 9)), black) + + def test_GET_PIXELVALS(self): + # surface.h GET_PIXELVALS bug regarding whether of not + # a surface has per-pixel alpha. Looking at the Amask + # is not enough. The surface's SRCALPHA flag must also + # be considered. Fix rev. 1923. + src = self._make_surface(32, srcalpha=True) + src.fill((0, 0, 0, 128)) + src.set_alpha(None) # Clear SRCALPHA flag. + dst = self._make_surface(32, srcalpha=True) + dst.blit(src, (0, 0), special_flags=BLEND_RGBA_ADD) + self.assertEqual(dst.get_at((0, 0)), (0, 0, 0, 255)) + + def test_fill_blend(self): + destinations = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + blend = [ + ("BLEND_ADD", (0, 25, 100, 255), lambda a, b: min(a + b, 255)), + ("BLEND_SUB", (0, 25, 100, 255), lambda a, b: max(a - b, 0)), + ("BLEND_MULT", (0, 7, 100, 255), lambda a, b: (a * b) // 256), + ("BLEND_MIN", (0, 255, 0, 255), min), + ("BLEND_MAX", (0, 255, 0, 255), max), + ] + + for dst in destinations: + dst_palette = [dst.unmap_rgb(dst.map_rgb(c)) for c in self._test_palette] + for blend_name, fill_color, op in blend: + fc = dst.unmap_rgb(dst.map_rgb(fill_color)) + self._fill_surface(dst) + p = [] + for dc in dst_palette: + c = [op(dc[i], fc[i]) for i in range(3)] + if dst.get_masks()[3]: + c.append(dc[3]) + else: + c.append(255) + c = dst.unmap_rgb(dst.map_rgb(c)) + p.append(c) + dst.fill(fill_color, special_flags=getattr(pygame, blend_name)) + self._assert_surface(dst, p, ", %s" % blend_name) + + def test_fill_blend_rgba(self): + destinations = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + blend = [ + ("BLEND_RGBA_ADD", (0, 25, 100, 255), lambda a, b: min(a + b, 255)), + ("BLEND_RGBA_SUB", (0, 25, 100, 255), lambda a, b: max(a - b, 0)), + ("BLEND_RGBA_MULT", (0, 7, 100, 255), lambda a, b: (a * b) // 256), + ("BLEND_RGBA_MIN", (0, 255, 0, 255), min), + ("BLEND_RGBA_MAX", (0, 255, 0, 255), max), + ] + + for dst in destinations: + dst_palette = [dst.unmap_rgb(dst.map_rgb(c)) for c in self._test_palette] + for blend_name, fill_color, op in blend: + fc = dst.unmap_rgb(dst.map_rgb(fill_color)) + self._fill_surface(dst) + p = [] + for dc in dst_palette: + c = [op(dc[i], fc[i]) for i in range(4)] + if not dst.get_masks()[3]: + c[3] = 255 + c = dst.unmap_rgb(dst.map_rgb(c)) + p.append(c) + dst.fill(fill_color, special_flags=getattr(pygame, blend_name)) + self._assert_surface(dst, p, ", %s" % blend_name) + + +class SurfaceSelfBlitTest(unittest.TestCase): + """Blit to self tests. + + This test case is in response to MotherHamster Bugzilla Bug 19. + """ + + def setUp(self): + # Needed for 8 bits-per-pixel color palette surface tests. + pygame.display.init() + + def tearDown(self): + pygame.display.quit() + + _test_palette = [(0, 0, 0, 255), (255, 0, 0, 0), (0, 255, 0, 255)] + surf_size = (9, 6) + + def _fill_surface(self, surf, palette=None): + if palette is None: + palette = self._test_palette + surf.fill(palette[1]) + surf.fill(palette[2], (1, 2, 1, 2)) + + def _make_surface(self, bitsize, srcalpha=False, palette=None): + if palette is None: + palette = self._test_palette + flags = 0 + if srcalpha: + flags |= SRCALPHA + surf = pygame.Surface(self.surf_size, flags, bitsize) + if bitsize == 8: + surf.set_palette([c[:3] for c in palette]) + self._fill_surface(surf, palette) + return surf + + def _assert_same(self, a, b): + w, h = a.get_size() + for x in range(w): + for y in range(h): + self.assertEqual( + a.get_at((x, y)), + b.get_at((x, y)), + ( + "%s != %s, bpp: %i" + % (a.get_at((x, y)), b.get_at((x, y)), a.get_bitsize()) + ), + ) + + def test_overlap_check(self): + # Ensure overlapping blits are properly detected. There are two + # places where this is done, within SoftBlitPyGame() in alphablit.c + # and PySurface_Blit() in surface.c. SoftBlitPyGame should catch the + # per-pixel alpha surface, PySurface_Blit the colorkey and blanket + # alpha surface. per-pixel alpha and blanket alpha self blits are + # not properly handled by SDL 1.2.13, so Pygame does them. + bgc = (0, 0, 0, 255) + rectc_left = (128, 64, 32, 255) + rectc_right = (255, 255, 255, 255) + colors = [(255, 255, 255, 255), (128, 64, 32, 255)] + overlaps = [ + (0, 0, 1, 0, (50, 0)), + (0, 0, 49, 1, (98, 2)), + (0, 0, 49, 49, (98, 98)), + (49, 0, 0, 1, (0, 2)), + (49, 0, 0, 49, (0, 98)), + ] + surfs = [pygame.Surface((100, 100), SRCALPHA, 32)] + surf = pygame.Surface((100, 100), 0, 32) + surf.set_alpha(255) + surfs.append(surf) + surf = pygame.Surface((100, 100), 0, 32) + surf.set_colorkey((0, 1, 0)) + surfs.append(surf) + for surf in surfs: + for s_x, s_y, d_x, d_y, test_posn in overlaps: + surf.fill(bgc) + surf.fill(rectc_right, (25, 0, 25, 50)) + surf.fill(rectc_left, (0, 0, 25, 50)) + surf.blit(surf, (d_x, d_y), (s_x, s_y, 50, 50)) + self.assertEqual(surf.get_at(test_posn), rectc_right) + + # https://github.com/pygame/pygame/issues/370#issuecomment-364625291 + @unittest.skipIf("ppc64le" in platform.uname(), "known ppc64le issue") + def test_colorkey(self): + # Check a workaround for an SDL 1.2.13 surface self-blit problem + # (MotherHamster Bugzilla bug 19). + pygame.display.set_mode((100, 50)) # Needed for 8bit surface + bitsizes = [8, 16, 24, 32] + for bitsize in bitsizes: + surf = self._make_surface(bitsize) + surf.set_colorkey(self._test_palette[1]) + surf.blit(surf, (3, 0)) + p = [] + for c in self._test_palette: + c = surf.unmap_rgb(surf.map_rgb(c)) + p.append(c) + p[1] = (p[1][0], p[1][1], p[1][2], 0) + tmp = self._make_surface(32, srcalpha=True, palette=p) + tmp.blit(tmp, (3, 0)) + tmp.set_alpha(None) + comp = self._make_surface(bitsize) + comp.blit(tmp, (0, 0)) + self._assert_same(surf, comp) + + # https://github.com/pygame/pygame/issues/370#issuecomment-364625291 + @unittest.skipIf("ppc64le" in platform.uname(), "known ppc64le issue") + def test_blanket_alpha(self): + # Check a workaround for an SDL 1.2.13 surface self-blit problem + # (MotherHamster Bugzilla bug 19). + pygame.display.set_mode((100, 50)) # Needed for 8bit surface + bitsizes = [8, 16, 24, 32] + for bitsize in bitsizes: + surf = self._make_surface(bitsize) + surf.set_alpha(128) + surf.blit(surf, (3, 0)) + p = [] + for c in self._test_palette: + c = surf.unmap_rgb(surf.map_rgb(c)) + p.append((c[0], c[1], c[2], 128)) + tmp = self._make_surface(32, srcalpha=True, palette=p) + tmp.blit(tmp, (3, 0)) + tmp.set_alpha(None) + comp = self._make_surface(bitsize) + comp.blit(tmp, (0, 0)) + self._assert_same(surf, comp) + + def test_pixel_alpha(self): + bitsizes = [16, 32] + for bitsize in bitsizes: + surf = self._make_surface(bitsize, srcalpha=True) + comp = self._make_surface(bitsize, srcalpha=True) + comp.blit(surf, (3, 0)) + surf.blit(surf, (3, 0)) + self._assert_same(surf, comp) + + def test_blend(self): + bitsizes = [8, 16, 24, 32] + blends = ["BLEND_ADD", "BLEND_SUB", "BLEND_MULT", "BLEND_MIN", "BLEND_MAX"] + for bitsize in bitsizes: + surf = self._make_surface(bitsize) + comp = self._make_surface(bitsize) + for blend in blends: + self._fill_surface(surf) + self._fill_surface(comp) + comp.blit(surf, (3, 0), special_flags=getattr(pygame, blend)) + surf.blit(surf, (3, 0), special_flags=getattr(pygame, blend)) + self._assert_same(surf, comp) + + def test_blend_rgba(self): + bitsizes = [16, 32] + blends = [ + "BLEND_RGBA_ADD", + "BLEND_RGBA_SUB", + "BLEND_RGBA_MULT", + "BLEND_RGBA_MIN", + "BLEND_RGBA_MAX", + ] + for bitsize in bitsizes: + surf = self._make_surface(bitsize, srcalpha=True) + comp = self._make_surface(bitsize, srcalpha=True) + for blend in blends: + self._fill_surface(surf) + self._fill_surface(comp) + comp.blit(surf, (3, 0), special_flags=getattr(pygame, blend)) + surf.blit(surf, (3, 0), special_flags=getattr(pygame, blend)) + self._assert_same(surf, comp) + + def test_subsurface(self): + # Blitting a surface to its subsurface is allowed. + surf = self._make_surface(32, srcalpha=True) + comp = surf.copy() + comp.blit(surf, (3, 0)) + sub = surf.subsurface((3, 0, 6, 6)) + sub.blit(surf, (0, 0)) + del sub + self._assert_same(surf, comp) + # Blitting a subsurface to its owner is forbidden because of + # lock conficts. This limitation allows the overlap check + # in PySurface_Blit of alphablit.c to be simplified. + def do_blit(d, s): + d.blit(s, (0, 0)) + + sub = surf.subsurface((1, 1, 2, 2)) + self.assertRaises(pygame.error, do_blit, surf, sub) + + def test_copy_alpha(self): + """issue 581: alpha of surface copy with SRCALPHA is set to 0.""" + surf = pygame.Surface((16, 16), pygame.SRCALPHA, 32) + self.assertEqual(surf.get_alpha(), 255) + surf2 = surf.copy() + self.assertEqual(surf2.get_alpha(), 255) + + +class SurfaceFillTest(unittest.TestCase): + def setUp(self): + pygame.display.init() + + def tearDown(self): + pygame.display.quit() + + def test_fill(self): + screen = pygame.display.set_mode((640, 480)) + + # Green and blue test pattern + screen.fill((0, 255, 0), (0, 0, 320, 240)) + screen.fill((0, 255, 0), (320, 240, 320, 240)) + screen.fill((0, 0, 255), (320, 0, 320, 240)) + screen.fill((0, 0, 255), (0, 240, 320, 240)) + + # Now apply a clip rect, such that only the left side of the + # screen should be effected by blit operations. + screen.set_clip((0, 0, 320, 480)) + + # Test fills with each special flag, and additionally without any. + screen.fill((255, 0, 0, 127), (160, 0, 320, 30), 0) + screen.fill((255, 0, 0, 127), (160, 30, 320, 30), pygame.BLEND_ADD) + screen.fill((0, 127, 127, 127), (160, 60, 320, 30), pygame.BLEND_SUB) + screen.fill((0, 63, 63, 127), (160, 90, 320, 30), pygame.BLEND_MULT) + screen.fill((0, 127, 127, 127), (160, 120, 320, 30), pygame.BLEND_MIN) + screen.fill((127, 0, 0, 127), (160, 150, 320, 30), pygame.BLEND_MAX) + screen.fill((255, 0, 0, 127), (160, 180, 320, 30), pygame.BLEND_RGBA_ADD) + screen.fill((0, 127, 127, 127), (160, 210, 320, 30), pygame.BLEND_RGBA_SUB) + screen.fill((0, 63, 63, 127), (160, 240, 320, 30), pygame.BLEND_RGBA_MULT) + screen.fill((0, 127, 127, 127), (160, 270, 320, 30), pygame.BLEND_RGBA_MIN) + screen.fill((127, 0, 0, 127), (160, 300, 320, 30), pygame.BLEND_RGBA_MAX) + screen.fill((255, 0, 0, 127), (160, 330, 320, 30), pygame.BLEND_RGB_ADD) + screen.fill((0, 127, 127, 127), (160, 360, 320, 30), pygame.BLEND_RGB_SUB) + screen.fill((0, 63, 63, 127), (160, 390, 320, 30), pygame.BLEND_RGB_MULT) + screen.fill((0, 127, 127, 127), (160, 420, 320, 30), pygame.BLEND_RGB_MIN) + screen.fill((255, 0, 0, 127), (160, 450, 320, 30), pygame.BLEND_RGB_MAX) + + # Update the display so we can see the results + pygame.display.flip() + + # Compare colors on both sides of window + for y in range(5, 480, 10): + self.assertEqual(screen.get_at((10, y)), screen.get_at((330, 480 - y))) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/surfarray_tags.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/surfarray_tags.py new file mode 100644 index 0000000..baa535c --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/surfarray_tags.py @@ -0,0 +1,16 @@ +__tags__ = ["array"] + +exclude = False + +try: + import numpy +except ImportError: + exclude = True +else: + try: + import pygame.pixelcopy + except ImportError: + exclude = True + +if exclude: + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/surfarray_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/surfarray_test.py new file mode 100644 index 0000000..11696c2 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/surfarray_test.py @@ -0,0 +1,733 @@ +import unittest +import platform + +from numpy import ( + uint8, + uint16, + uint32, + uint64, + zeros, + float32, + float64, + alltrue, + rint, + arange, +) + +import pygame +from pygame.locals import * + +import pygame.surfarray + +arraytype = "numpy" + + +IS_PYPY = "PyPy" == platform.python_implementation() + + +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO +class SurfarrayModuleTest(unittest.TestCase): + pixels2d = {8: True, 16: True, 24: False, 32: True} + pixels3d = {8: False, 16: False, 24: True, 32: True} + array2d = {8: True, 16: True, 24: True, 32: True} + array3d = {8: False, 16: False, 24: True, 32: True} + + test_palette = [ + (0, 0, 0, 255), + (10, 30, 60, 255), + (25, 75, 100, 255), + (100, 150, 200, 255), + (0, 100, 200, 255), + ] + surf_size = (10, 12) + test_points = [ + ((0, 0), 1), + ((4, 5), 1), + ((9, 0), 2), + ((5, 5), 2), + ((0, 11), 3), + ((4, 6), 3), + ((9, 11), 4), + ((5, 6), 4), + ] + + @classmethod + def setUpClass(cls): + # Needed for 8 bits-per-pixel color palette surface tests. + pygame.init() + + @classmethod + def tearDownClass(cls): + pygame.quit() + + def setUp(cls): + # This makes sure pygame is always initialized before each test (in + # case a test calls pygame.quit()). + if not pygame.get_init(): + pygame.init() + + # Makes sure the same array package is used each time. + pygame.surfarray.use_arraytype(arraytype) + + def _make_surface(self, bitsize, srcalpha=False, palette=None): + if palette is None: + palette = self.test_palette + flags = 0 + if srcalpha: + flags |= SRCALPHA + surf = pygame.Surface(self.surf_size, flags, bitsize) + if bitsize == 8: + surf.set_palette([c[:3] for c in palette]) + return surf + + def _fill_surface(self, surf, palette=None): + if palette is None: + palette = self.test_palette + surf.fill(palette[1], (0, 0, 5, 6)) + surf.fill(palette[2], (5, 0, 5, 6)) + surf.fill(palette[3], (0, 6, 5, 6)) + surf.fill(palette[4], (5, 6, 5, 6)) + + def _make_src_surface(self, bitsize, srcalpha=False, palette=None): + surf = self._make_surface(bitsize, srcalpha, palette) + self._fill_surface(surf, palette) + return surf + + def _assert_surface(self, surf, palette=None, msg=""): + if palette is None: + palette = self.test_palette + if surf.get_bitsize() == 16: + palette = [surf.unmap_rgb(surf.map_rgb(c)) for c in palette] + for posn, i in self.test_points: + self.assertEqual( + surf.get_at(posn), + palette[i], + "%s != %s: flags: %i, bpp: %i, posn: %s%s" + % ( + surf.get_at(posn), + palette[i], + surf.get_flags(), + surf.get_bitsize(), + posn, + msg, + ), + ) + + def _make_array3d(self, dtype): + return zeros((self.surf_size[0], self.surf_size[1], 3), dtype) + + def _fill_array2d(self, arr, surf): + palette = self.test_palette + arr[:5, :6] = surf.map_rgb(palette[1]) + arr[5:, :6] = surf.map_rgb(palette[2]) + arr[:5, 6:] = surf.map_rgb(palette[3]) + arr[5:, 6:] = surf.map_rgb(palette[4]) + + def _fill_array3d(self, arr): + palette = self.test_palette + arr[:5, :6] = palette[1][:3] + arr[5:, :6] = palette[2][:3] + arr[:5, 6:] = palette[3][:3] + arr[5:, 6:] = palette[4][:3] + + def _make_src_array3d(self, dtype): + arr = self._make_array3d(dtype) + self._fill_array3d(arr) + return arr + + def _make_array2d(self, dtype): + return zeros(self.surf_size, dtype) + + def test_array2d(self): + + sources = [ + self._make_src_surface(8), + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] + palette = self.test_palette + alpha_color = (0, 0, 0, 128) + + for surf in sources: + arr = pygame.surfarray.array2d(surf) + for posn, i in self.test_points: + self.assertEqual( + arr[posn], + surf.get_at_mapped(posn), + "%s != %s: flags: %i, bpp: %i, posn: %s" + % ( + arr[posn], + surf.get_at_mapped(posn), + surf.get_flags(), + surf.get_bitsize(), + posn, + ), + ) + + if surf.get_masks()[3]: + surf.fill(alpha_color) + arr = pygame.surfarray.array2d(surf) + posn = (0, 0) + self.assertEqual( + arr[posn], + surf.get_at_mapped(posn), + "%s != %s: bpp: %i" + % (arr[posn], surf.get_at_mapped(posn), surf.get_bitsize()), + ) + + def test_array3d(self): + + sources = [ + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] + palette = self.test_palette + + for surf in sources: + arr = pygame.surfarray.array3d(surf) + + def same_color(ac, sc): + return ac[0] == sc[0] and ac[1] == sc[1] and ac[2] == sc[2] + + for posn, i in self.test_points: + self.assertTrue( + same_color(arr[posn], surf.get_at(posn)), + "%s != %s: flags: %i, bpp: %i, posn: %s" + % ( + tuple(arr[posn]), + surf.get_at(posn), + surf.get_flags(), + surf.get_bitsize(), + posn, + ), + ) + + def test_array_alpha(self): + + palette = [ + (0, 0, 0, 0), + (10, 50, 100, 255), + (60, 120, 240, 130), + (64, 128, 255, 0), + (255, 128, 0, 65), + ] + targets = [ + self._make_src_surface(8, palette=palette), + self._make_src_surface(16, palette=palette), + self._make_src_surface(16, palette=palette, srcalpha=True), + self._make_src_surface(24, palette=palette), + self._make_src_surface(32, palette=palette), + self._make_src_surface(32, palette=palette, srcalpha=True), + ] + + for surf in targets: + p = palette + if surf.get_bitsize() == 16: + p = [surf.unmap_rgb(surf.map_rgb(c)) for c in p] + arr = pygame.surfarray.array_alpha(surf) + if surf.get_masks()[3]: + for (x, y), i in self.test_points: + self.assertEqual( + arr[x, y], + p[i][3], + ( + "%i != %i, posn: (%i, %i), " + "bitsize: %i" + % (arr[x, y], p[i][3], x, y, surf.get_bitsize()) + ), + ) + else: + self.assertTrue(alltrue(arr == 255)) + + # No per-pixel alpha when blanket alpha is None. + for surf in targets: + blanket_alpha = surf.get_alpha() + surf.set_alpha(None) + arr = pygame.surfarray.array_alpha(surf) + self.assertTrue( + alltrue(arr == 255), + "All alpha values should be 255 when" + " surf.set_alpha(None) has been set." + " bitsize: %i, flags: %i" % (surf.get_bitsize(), surf.get_flags()), + ) + surf.set_alpha(blanket_alpha) + + # Bug for per-pixel alpha surface when blanket alpha 0. + for surf in targets: + blanket_alpha = surf.get_alpha() + surf.set_alpha(0) + arr = pygame.surfarray.array_alpha(surf) + if surf.get_masks()[3]: + self.assertFalse( + alltrue(arr == 255), + "bitsize: %i, flags: %i" % (surf.get_bitsize(), surf.get_flags()), + ) + else: + self.assertTrue( + alltrue(arr == 255), + "bitsize: %i, flags: %i" % (surf.get_bitsize(), surf.get_flags()), + ) + surf.set_alpha(blanket_alpha) + + def test_array_colorkey(self): + + palette = [ + (0, 0, 0, 0), + (10, 50, 100, 255), + (60, 120, 240, 130), + (64, 128, 255, 0), + (255, 128, 0, 65), + ] + targets = [ + self._make_src_surface(8, palette=palette), + self._make_src_surface(16, palette=palette), + self._make_src_surface(16, palette=palette, srcalpha=True), + self._make_src_surface(24, palette=palette), + self._make_src_surface(32, palette=palette), + self._make_src_surface(32, palette=palette, srcalpha=True), + ] + + for surf in targets: + p = palette + if surf.get_bitsize() == 16: + p = [surf.unmap_rgb(surf.map_rgb(c)) for c in p] + surf.set_colorkey(None) + arr = pygame.surfarray.array_colorkey(surf) + self.assertTrue(alltrue(arr == 255)) + + for i in range(1, len(palette)): + surf.set_colorkey(p[i]) + alphas = [255] * len(p) + alphas[i] = 0 + arr = pygame.surfarray.array_colorkey(surf) + for (x, y), j in self.test_points: + self.assertEqual( + arr[x, y], + alphas[j], + ( + "%i != %i, posn: (%i, %i), " + "bitsize: %i" + % (arr[x, y], alphas[j], x, y, surf.get_bitsize()) + ), + ) + + def test_blit_array(self): + + # bug 24 at http://pygame.motherhamster.org/bugzilla/ + if "numpy" in pygame.surfarray.get_arraytypes(): + prev = pygame.surfarray.get_arraytype() + # This would raise exception: + # File "[...]\pygame\_numpysurfarray.py", line 381, in blit_array + # (array[:,:,1::3] >> losses[1] << shifts[1]) | \ + # TypeError: unsupported operand type(s) for >>: 'float' and 'int' + pygame.surfarray.use_arraytype("numpy") + s = pygame.Surface((10, 10), 0, 24) + a = pygame.surfarray.array3d(s) + pygame.surfarray.blit_array(s, a) + prev = pygame.surfarray.use_arraytype(prev) + + # target surfaces + targets = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + + # source arrays + arrays3d = [] + dtypes = [(8, uint8), (16, uint16), (32, uint32)] + try: + dtypes.append((64, uint64)) + except NameError: + pass + arrays3d = [(self._make_src_array3d(dtype), None) for __, dtype in dtypes] + for bitsize in [8, 16, 24, 32]: + palette = None + if bitsize == 16: + s = pygame.Surface((1, 1), 0, 16) + palette = [s.unmap_rgb(s.map_rgb(c)) for c in self.test_palette] + if self.pixels3d[bitsize]: + surf = self._make_src_surface(bitsize) + arr = pygame.surfarray.pixels3d(surf) + arrays3d.append((arr, palette)) + if self.array3d[bitsize]: + surf = self._make_src_surface(bitsize) + arr = pygame.surfarray.array3d(surf) + arrays3d.append((arr, palette)) + for sz, dtype in dtypes: + arrays3d.append((arr.astype(dtype), palette)) + + # tests on arrays + def do_blit(surf, arr): + pygame.surfarray.blit_array(surf, arr) + + for surf in targets: + bitsize = surf.get_bitsize() + for arr, palette in arrays3d: + surf.fill((0, 0, 0, 0)) + if bitsize == 8: + self.assertRaises(ValueError, do_blit, surf, arr) + else: + pygame.surfarray.blit_array(surf, arr) + self._assert_surface(surf, palette) + + if self.pixels2d[bitsize]: + surf.fill((0, 0, 0, 0)) + s = self._make_src_surface(bitsize, surf.get_flags() & SRCALPHA) + arr = pygame.surfarray.pixels2d(s) + pygame.surfarray.blit_array(surf, arr) + self._assert_surface(surf) + + if self.array2d[bitsize]: + s = self._make_src_surface(bitsize, surf.get_flags() & SRCALPHA) + arr = pygame.surfarray.array2d(s) + for sz, dtype in dtypes: + surf.fill((0, 0, 0, 0)) + if sz >= bitsize: + pygame.surfarray.blit_array(surf, arr.astype(dtype)) + self._assert_surface(surf) + else: + self.assertRaises( + ValueError, do_blit, surf, self._make_array2d(dtype) + ) + + # Check alpha for 2D arrays + surf = self._make_surface(16, srcalpha=True) + arr = zeros(surf.get_size(), uint16) + arr[...] = surf.map_rgb((0, 128, 255, 64)) + color = surf.unmap_rgb(arr[0, 0]) + pygame.surfarray.blit_array(surf, arr) + self.assertEqual(surf.get_at((5, 5)), color) + + surf = self._make_surface(32, srcalpha=True) + arr = zeros(surf.get_size(), uint32) + color = (0, 111, 255, 63) + arr[...] = surf.map_rgb(color) + pygame.surfarray.blit_array(surf, arr) + self.assertEqual(surf.get_at((5, 5)), color) + + # Check shifts + arr3d = self._make_src_array3d(uint8) + + shift_tests = [ + (16, [12, 0, 8, 4], [0xF000, 0xF, 0xF00, 0xF0]), + (24, [16, 0, 8, 0], [0xFF0000, 0xFF, 0xFF00, 0]), + (32, [0, 16, 24, 8], [0xFF, 0xFF0000, 0xFF000000, 0xFF00]), + ] + + for bitsize, shifts, masks in shift_tests: + surf = self._make_surface(bitsize, srcalpha=(shifts[3] != 0)) + palette = None + if bitsize == 16: + palette = [surf.unmap_rgb(surf.map_rgb(c)) for c in self.test_palette] + + if pygame.get_sdl_version()[0] == 1: + surf.set_shifts(shifts) + surf.set_masks(masks) + pygame.surfarray.blit_array(surf, arr3d) + self._assert_surface(surf, palette) + else: + self.assertRaises(TypeError, surf.set_shifts, shifts) + self.assertRaises(TypeError, surf.set_masks, masks) + + # Invalid arrays + surf = pygame.Surface((1, 1), 0, 32) + t = "abcd" + self.assertRaises(ValueError, do_blit, surf, t) + + surf_size = self.surf_size + surf = pygame.Surface(surf_size, 0, 32) + arr = zeros([surf_size[0], surf_size[1] + 1, 3], uint32) + self.assertRaises(ValueError, do_blit, surf, arr) + arr = zeros([surf_size[0] + 1, surf_size[1], 3], uint32) + self.assertRaises(ValueError, do_blit, surf, arr) + + surf = pygame.Surface((1, 4), 0, 32) + arr = zeros((4,), uint32) + self.assertRaises(ValueError, do_blit, surf, arr) + arr.shape = (1, 1, 1, 4) + self.assertRaises(ValueError, do_blit, surf, arr) + + # Issue #81: round from float to int + try: + rint + except NameError: + pass + else: + surf = pygame.Surface((10, 10), pygame.SRCALPHA, 32) + w, h = surf.get_size() + length = w * h + for dtype in [float32, float64]: + surf.fill((255, 255, 255, 0)) + farr = arange(0, length, dtype=dtype) + farr.shape = w, h + pygame.surfarray.blit_array(surf, farr) + for x in range(w): + for y in range(h): + self.assertEqual( + surf.get_at_mapped((x, y)), int(rint(farr[x, y])) + ) + + def test_get_arraytype(self): + array_type = pygame.surfarray.get_arraytype() + + self.assertEqual(array_type, "numpy", "unknown array type %s" % array_type) + + def test_get_arraytypes(self): + + arraytypes = pygame.surfarray.get_arraytypes() + self.assertIn("numpy", arraytypes) + + for atype in arraytypes: + self.assertEqual(atype, "numpy", "unknown array type %s" % atype) + + def test_make_surface(self): + + # How does one properly test this with 2d arrays. It makes no sense + # since the pixel format is not entirely dependent on element size. + # Just make sure the surface pixel size is at least as large as the + # array element size I guess. + # + for bitsize, dtype in [(8, uint8), (16, uint16), (24, uint32)]: + ## Even this simple assertion fails for 2d arrays. Where's the problem? + ## surf = pygame.surfarray.make_surface(self._make_array2d(dtype)) + ## self.assertGreaterEqual(surf.get_bitsize(), bitsize, + ## "not %i >= %i)" % (surf.get_bitsize(), bitsize)) + ## + surf = pygame.surfarray.make_surface(self._make_src_array3d(dtype)) + self._assert_surface(surf) + + # Issue #81: round from float to int + try: + rint + except NameError: + pass + else: + w = 9 + h = 11 + length = w * h + for dtype in [float32, float64]: + farr = arange(0, length, dtype=dtype) + farr.shape = w, h + surf = pygame.surfarray.make_surface(farr) + for x in range(w): + for y in range(h): + self.assertEqual( + surf.get_at_mapped((x, y)), int(rint(farr[x, y])) + ) + + def test_map_array(self): + + arr3d = self._make_src_array3d(uint8) + targets = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + palette = self.test_palette + + for surf in targets: + arr2d = pygame.surfarray.map_array(surf, arr3d) + for posn, i in self.test_points: + self.assertEqual( + arr2d[posn], + surf.map_rgb(palette[i]), + "%i != %i, bitsize: %i, flags: %i" + % ( + arr2d[posn], + surf.map_rgb(palette[i]), + surf.get_bitsize(), + surf.get_flags(), + ), + ) + + # Exception checks + self.assertRaises( + ValueError, + pygame.surfarray.map_array, + self._make_surface(32), + self._make_array2d(uint8), + ) + + def test_pixels2d(self): + + sources = [ + self._make_surface(8), + self._make_surface(16, srcalpha=True), + self._make_surface(32, srcalpha=True), + ] + + for surf in sources: + self.assertFalse(surf.get_locked()) + arr = pygame.surfarray.pixels2d(surf) + self.assertTrue(surf.get_locked()) + self._fill_array2d(arr, surf) + surf.unlock() + self.assertTrue(surf.get_locked()) + del arr + self.assertFalse(surf.get_locked()) + self.assertEqual(surf.get_locks(), ()) + self._assert_surface(surf) + + # Error checks + self.assertRaises(ValueError, pygame.surfarray.pixels2d, self._make_surface(24)) + + def test_pixels3d(self): + + sources = [self._make_surface(24), self._make_surface(32)] + + for surf in sources: + self.assertFalse(surf.get_locked()) + arr = pygame.surfarray.pixels3d(surf) + self.assertTrue(surf.get_locked()) + self._fill_array3d(arr) + surf.unlock() + self.assertTrue(surf.get_locked()) + del arr + self.assertFalse(surf.get_locked()) + self.assertEqual(surf.get_locks(), ()) + self._assert_surface(surf) + + # Alpha check + color = (1, 2, 3, 0) + surf = self._make_surface(32, srcalpha=True) + arr = pygame.surfarray.pixels3d(surf) + arr[0, 0] = color[:3] + self.assertEqual(surf.get_at((0, 0)), color) + + # Error checks + def do_pixels3d(surf): + pygame.surfarray.pixels3d(surf) + + self.assertRaises(ValueError, do_pixels3d, self._make_surface(8)) + self.assertRaises(ValueError, do_pixels3d, self._make_surface(16)) + + def test_pixels_alpha(self): + + palette = [ + (0, 0, 0, 0), + (127, 127, 127, 0), + (127, 127, 127, 85), + (127, 127, 127, 170), + (127, 127, 127, 255), + ] + alphas = [0, 45, 86, 99, 180] + + surf = self._make_src_surface(32, srcalpha=True, palette=palette) + + self.assertFalse(surf.get_locked()) + arr = pygame.surfarray.pixels_alpha(surf) + self.assertTrue(surf.get_locked()) + surf.unlock() + self.assertTrue(surf.get_locked()) + + for (x, y), i in self.test_points: + self.assertEqual(arr[x, y], palette[i][3]) + + for (x, y), i in self.test_points: + alpha = alphas[i] + arr[x, y] = alpha + color = (127, 127, 127, alpha) + self.assertEqual(surf.get_at((x, y)), color, "posn: (%i, %i)" % (x, y)) + + del arr + self.assertFalse(surf.get_locked()) + self.assertEqual(surf.get_locks(), ()) + + # Check exceptions. + def do_pixels_alpha(surf): + pygame.surfarray.pixels_alpha(surf) + + targets = [(8, False), (16, False), (16, True), (24, False), (32, False)] + + for bitsize, srcalpha in targets: + self.assertRaises( + ValueError, do_pixels_alpha, self._make_surface(bitsize, srcalpha) + ) + + def test_pixels_red(self): + self._test_pixels_rgb("red", 0) + + def test_pixels_green(self): + self._test_pixels_rgb("green", 1) + + def test_pixels_blue(self): + self._test_pixels_rgb("blue", 2) + + def _test_pixels_rgb(self, operation, mask_posn): + method_name = "pixels_" + operation + + pixels_rgb = getattr(pygame.surfarray, method_name) + palette = [ + (0, 0, 0, 255), + (5, 13, 23, 255), + (29, 31, 37, 255), + (131, 157, 167, 255), + (179, 191, 251, 255), + ] + plane = [c[mask_posn] for c in palette] + + surf24 = self._make_src_surface(24, srcalpha=False, palette=palette) + surf32 = self._make_src_surface(32, srcalpha=False, palette=palette) + surf32a = self._make_src_surface(32, srcalpha=True, palette=palette) + + for surf in [surf24, surf32, surf32a]: + self.assertFalse(surf.get_locked()) + arr = pixels_rgb(surf) + self.assertTrue(surf.get_locked()) + surf.unlock() + self.assertTrue(surf.get_locked()) + + for (x, y), i in self.test_points: + self.assertEqual(arr[x, y], plane[i]) + + del arr + self.assertFalse(surf.get_locked()) + self.assertEqual(surf.get_locks(), ()) + + # Check exceptions. + targets = [(8, False), (16, False), (16, True)] + + for bitsize, srcalpha in targets: + self.assertRaises( + ValueError, pixels_rgb, self._make_surface(bitsize, srcalpha) + ) + + def test_use_arraytype(self): + def do_use_arraytype(atype): + pygame.surfarray.use_arraytype(atype) + + pygame.surfarray.use_arraytype("numpy") + self.assertEqual(pygame.surfarray.get_arraytype(), "numpy") + self.assertRaises(ValueError, do_use_arraytype, "not an option") + + def test_surf_lock(self): + sf = pygame.Surface((5, 5), 0, 32) + for atype in pygame.surfarray.get_arraytypes(): + pygame.surfarray.use_arraytype(atype) + + ar = pygame.surfarray.pixels2d(sf) + self.assertTrue(sf.get_locked()) + + sf.unlock() + self.assertTrue(sf.get_locked()) + + del ar + self.assertFalse(sf.get_locked()) + self.assertEqual(sf.get_locks(), ()) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/surflock_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/surflock_test.py new file mode 100644 index 0000000..19f354b --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/surflock_test.py @@ -0,0 +1,144 @@ +import unittest +import sys +import platform + +import pygame + +IS_PYPY = "PyPy" == platform.python_implementation() + + +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO +class SurfaceLockTest(unittest.TestCase): + def test_lock(self): + sf = pygame.Surface((5, 5)) + + sf.lock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (sf,)) + + sf.lock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (sf, sf)) + + sf.unlock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (sf,)) + + sf.unlock() + self.assertEqual(sf.get_locked(), False) + self.assertEqual(sf.get_locks(), ()) + + def test_subsurface_lock(self): + sf = pygame.Surface((5, 5)) + subsf = sf.subsurface((1, 1, 2, 2)) + sf2 = pygame.Surface((5, 5)) + + # Simple blits, nothing should happen here. + sf2.blit(subsf, (0, 0)) + sf2.blit(sf, (0, 0)) + + # Test blitting on self: + self.assertRaises(pygame.error, sf.blit, subsf, (0, 0)) + # self.assertRaises(pygame.error, subsf.blit, sf, (0, 0)) + # ^ Fails although it should not in my opinion. If I cannot + # blit the subsurface to the surface, it should not be allowed + # the other way around as well. + + # Test additional locks. + sf.lock() + sf2.blit(subsf, (0, 0)) + self.assertRaises(pygame.error, sf2.blit, sf, (0, 0)) + + subsf.lock() + self.assertRaises(pygame.error, sf2.blit, subsf, (0, 0)) + self.assertRaises(pygame.error, sf2.blit, sf, (0, 0)) + + # sf and subsf are now explicitly locked. Unlock sf, so we can + # (assume) to blit it. + # It will fail though as the subsurface still has a lock around, + # which is okay and correct behaviour. + sf.unlock() + self.assertRaises(pygame.error, sf2.blit, subsf, (0, 0)) + self.assertRaises(pygame.error, sf2.blit, sf, (0, 0)) + + # Run a second unlock on the surface. This should ideally have + # no effect as the subsurface is the locking reason! + sf.unlock() + self.assertRaises(pygame.error, sf2.blit, sf, (0, 0)) + self.assertRaises(pygame.error, sf2.blit, subsf, (0, 0)) + subsf.unlock() + + sf.lock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (sf,)) + self.assertEqual(subsf.get_locked(), False) + self.assertEqual(subsf.get_locks(), ()) + + subsf.lock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (sf, subsf)) + self.assertEqual(subsf.get_locked(), True) + self.assertEqual(subsf.get_locks(), (subsf,)) + + sf.unlock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (subsf,)) + self.assertEqual(subsf.get_locked(), True) + self.assertEqual(subsf.get_locks(), (subsf,)) + + subsf.unlock() + self.assertEqual(sf.get_locked(), False) + self.assertEqual(sf.get_locks(), ()) + self.assertEqual(subsf.get_locked(), False) + self.assertEqual(subsf.get_locks(), ()) + + subsf.lock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (subsf,)) + self.assertEqual(subsf.get_locked(), True) + self.assertEqual(subsf.get_locks(), (subsf,)) + + subsf.lock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (subsf, subsf)) + self.assertEqual(subsf.get_locked(), True) + self.assertEqual(subsf.get_locks(), (subsf, subsf)) + + def test_pxarray_ref(self): + sf = pygame.Surface((5, 5)) + ar = pygame.PixelArray(sf) + ar2 = pygame.PixelArray(sf) + + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (ar, ar2)) + + del ar + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (ar2,)) + + ar = ar2[:] + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (ar2,)) + + del ar + self.assertEqual(sf.get_locked(), True) + self.assertEqual(len(sf.get_locks()), 1) + + def test_buffer(self): + sf = pygame.Surface((5, 5)) + buf = sf.get_buffer() + + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (buf,)) + + sf.unlock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (buf,)) + + del buf + self.assertEqual(sf.get_locked(), False) + self.assertEqual(sf.get_locks(), ()) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/sysfont_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/sysfont_test.py new file mode 100644 index 0000000..d8fb949 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/sysfont_test.py @@ -0,0 +1,34 @@ +import unittest +import platform + + +class SysfontModuleTest(unittest.TestCase): + def todo_test_create_aliases(self): + self.fail() + + def todo_test_initsysfonts(self): + self.fail() + + @unittest.skipIf("Darwin" not in platform.platform(), "Not mac we skip.") + def test_initsysfonts_darwin(self): + import pygame.sysfont + + self.assertTrue(len(pygame.sysfont.get_fonts()) > 10) + + def test_sysfont(self): + import pygame.font + + pygame.font.init() + arial = pygame.font.SysFont("Arial", 40) + + def todo_test_initsysfonts_unix(self): + self.fail() + + def todo_test_initsysfonts_win32(self): + self.fail() + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_test_.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_test_.py new file mode 100644 index 0000000..0880e7e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_test_.py @@ -0,0 +1,2 @@ +while True: + pass diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/__init__.py new file mode 100644 index 0000000..808590e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/__init__.py @@ -0,0 +1,246 @@ +#################################### IMPORTS ################################### + +is_pygame_pkg = __name__.startswith("pygame.tests.") + +import tempfile, sys, pygame, time, os + +################################################################################ +# Python 3.x compatibility +try: + xrange_ = xrange +except NameError: + xrange_ = range + +try: + raw_input_ = raw_input +except NameError: + raw_input_ = input + + +if sys.version_info[0] == 3: + def tostring(row): + """Convert row of bytes to string. Expects `row` to be an + ``array``. + """ + return row.tobytes() + +else: + def tostring(row): + """Convert row of bytes to string. Expects `row` to be an + ``array``. + """ + return row.tostring() + + +def geterror(): + return sys.exc_info()[1] + + +class AssertRaisesRegexMixin(object): + """Provides a way to prevent DeprecationWarnings in python >= 3.2. + + For this mixin to override correctly it needs to be before the + unittest.TestCase in the multiple inheritance hierarchy. + e.g. class TestClass(AssertRaisesRegexMixin, unittest.TestCase) + + This class/mixin and its usage can be removed when pygame no longer + supports python < 3.2. + """ + + def assertRaisesRegex(self, *args, **kwargs): + try: + return super(AssertRaisesRegexMixin, self).assertRaisesRegex( + *args, **kwargs + ) + except AttributeError: + try: + return super(AssertRaisesRegexMixin, self).assertRaisesRegexp( + *args, **kwargs + ) + except AttributeError: + self.skipTest("No assertRaisesRegex/assertRaisesRegexp method") + + +################################################################################ + +this_dir = os.path.dirname(os.path.abspath(__file__)) +trunk_dir = os.path.split(os.path.split(this_dir)[0])[0] +if is_pygame_pkg: + test_module = "tests" +else: + test_module = "test" + + +def trunk_relative_path(relative): + return os.path.normpath(os.path.join(trunk_dir, relative)) + + +def fixture_path(path): + return trunk_relative_path(os.path.join(test_module, "fixtures", path)) + + +def example_path(path): + return trunk_relative_path(os.path.join("examples", path)) + + +sys.path.insert(0, trunk_relative_path(".")) + + +################################## TEMP FILES ################################## + + +def get_tmp_dir(): + return tempfile.mkdtemp() + + +################################################################################ + + +def question(q): + return raw_input_("\n%s (y/n): " % q.rstrip(" ")).lower().strip() == "y" + + +def prompt(p): + return raw_input_("\n%s (press enter to continue): " % p.rstrip(" ")) + + +#################################### HELPERS ################################### + + +def rgba_between(value, minimum=0, maximum=255): + if value < minimum: + return minimum + elif value > maximum: + return maximum + else: + return value + + +def combinations(seqs): + """ + + Recipe 496807 from ActiveState Python CookBook + + Non recursive technique for getting all possible combinations of a sequence + of sequences. + + """ + + r = [[]] + for x in seqs: + r = [i + [y] for y in x for i in r] + return r + + +def gradient(width, height): + """ + + Yields a pt and corresponding RGBA tuple, for every (width, height) combo. + Useful for generating gradients. + + Actual gradient may be changed, no tests rely on specific values. + + Used in transform.rotate lossless tests to generate a fixture. + + """ + + for l in xrange_(width): + for t in xrange_(height): + yield (l, t), tuple(map(rgba_between, (l, t, l, l + t))) + + +def rect_area_pts(rect): + for l in xrange_(rect.left, rect.right): + for t in xrange_(rect.top, rect.bottom): + yield l, t + + +def rect_perimeter_pts(rect): + """ + + Returns pts ((L, T) tuples) encompassing the perimeter of a rect. + + The order is clockwise: + + topleft to topright + topright to bottomright + bottomright to bottomleft + bottomleft to topleft + + Duplicate pts are not returned + + """ + clock_wise_from_top_left = ( + [(l, rect.top) for l in xrange_(rect.left, rect.right)], + [(rect.right - 1, t) for t in xrange_(rect.top + 1, rect.bottom)], + [(l, rect.bottom - 1) for l in xrange_(rect.right - 2, rect.left - 1, -1)], + [(rect.left, t) for t in xrange_(rect.bottom - 2, rect.top, -1)], + ) + + for line in clock_wise_from_top_left: + for pt in line: + yield pt + + +def rect_outer_bounds(rect): + """ + + Returns topleft outerbound if possible and then the other pts, that are + "exclusive" bounds of the rect + + ?------O + |RECT| ?|0)uterbound + |----| + O O + + """ + return ([(rect.left - 1, rect.top)] if rect.left else []) + [ + rect.topright, + rect.bottomleft, + rect.bottomright, + ] + + +def import_submodule(module): + m = __import__(module) + for n in module.split(".")[1:]: + m = getattr(m, n) + return m + + +class SurfaceSubclass(pygame.Surface): + """A subclassed Surface to test inheritance.""" + + def __init__(self, *args, **kwargs): + super(SurfaceSubclass, self).__init__(*args, **kwargs) + self.test_attribute = True + + +def test(): + """ + + Lightweight test for helpers + + """ + + r = pygame.Rect(0, 0, 10, 10) + assert rect_outer_bounds(r) == [(10, 0), (0, 10), (10, 10)] # tr # bl # br + + assert len(list(rect_area_pts(r))) == 100 + + r = pygame.Rect(0, 0, 3, 3) + assert list(rect_perimeter_pts(r)) == [ + (0, 0), + (1, 0), + (2, 0), # tl -> tr + (2, 1), + (2, 2), # tr -> br + (1, 2), + (0, 2), # br -> bl + (0, 1), # bl -> tl + ] + + print("Tests: OK") + + +################################################################################ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/arrinter.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/arrinter.py new file mode 100644 index 0000000..9b46b64 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/arrinter.py @@ -0,0 +1,451 @@ +import sys +import ctypes +from ctypes import * +import unittest + +__all__ = [ + "PAI_CONTIGUOUS", + "PAI_FORTRAN", + "PAI_ALIGNED", + "PAI_NOTSWAPPED", + "PAI_WRITEABLE", + "PAI_ARR_HAS_DESCR", + "ArrayInterface", +] + +try: + c_ssize_t # Undefined in early Python versions +except NameError: + if sizeof(c_uint) == sizeof(c_void_p): + c_size_t = c_uint + c_ssize_t = c_int + elif sizeof(c_ulong) == sizeof(c_void_p): + c_size_t = c_ulong + c_ssize_t = c_long + elif sizeof(c_ulonglong) == sizeof(c_void_p): + c_size_t = c_ulonglong + c_ssize_t = c_longlong + + +SIZEOF_VOID_P = sizeof(c_void_p) +if SIZEOF_VOID_P <= sizeof(c_int): + Py_intptr_t = c_int +elif SIZEOF_VOID_P <= sizeof(c_long): + Py_intptr_t = c_long +elif "c_longlong" in globals() and SIZEOF_VOID_P <= sizeof(c_longlong): + Py_intptr_t = c_longlong +else: + raise RuntimeError("Unrecognized pointer size %i" % (pointer_size,)) + + +class PyArrayInterface(Structure): + _fields_ = [ + ("two", c_int), + ("nd", c_int), + ("typekind", c_char), + ("itemsize", c_int), + ("flags", c_int), + ("shape", POINTER(Py_intptr_t)), + ("strides", POINTER(Py_intptr_t)), + ("data", c_void_p), + ("descr", py_object), + ] + + +PAI_Ptr = POINTER(PyArrayInterface) +try: + PyCObject_AsVoidPtr = pythonapi.PyCObject_AsVoidPtr +except AttributeError: + + def PyCObject_AsVoidPtr(o): + raise TypeError("Not available") + + +else: + PyCObject_AsVoidPtr.restype = c_void_p + PyCObject_AsVoidPtr.argtypes = [py_object] + PyCObject_GetDesc = pythonapi.PyCObject_GetDesc + PyCObject_GetDesc.restype = c_void_p + PyCObject_GetDesc.argtypes = [py_object] +try: + PyCapsule_IsValid = pythonapi.PyCapsule_IsValid +except AttributeError: + + def PyCapsule_IsValid(capsule, name): + return 0 + + +else: + PyCapsule_IsValid.restype = c_int + PyCapsule_IsValid.argtypes = [py_object, c_char_p] + PyCapsule_GetPointer = pythonapi.PyCapsule_GetPointer + PyCapsule_GetPointer.restype = c_void_p + PyCapsule_GetPointer.argtypes = [py_object, c_char_p] + PyCapsule_GetContext = pythonapi.PyCapsule_GetContext + PyCapsule_GetContext.restype = c_void_p + PyCapsule_GetContext.argtypes = [py_object] + +if sys.version_info >= (3,): # Python3 + PyCapsule_Destructor = CFUNCTYPE(None, py_object) + PyCapsule_New = pythonapi.PyCapsule_New + PyCapsule_New.restype = py_object + PyCapsule_New.argtypes = [c_void_p, c_char_p, POINTER(PyCapsule_Destructor)] + + def capsule_new(p): + return PyCapsule_New(addressof(p), None, None) + + +else: + PyCObject_Destructor = CFUNCTYPE(None, c_void_p) + PyCObject_FromVoidPtr = pythonapi.PyCObject_FromVoidPtr + PyCObject_FromVoidPtr.restype = py_object + PyCObject_FromVoidPtr.argtypes = [c_void_p, POINTER(PyCObject_Destructor)] + + def capsule_new(p): + return PyCObject_FromVoidPtr(addressof(p), None) + + +PAI_CONTIGUOUS = 0x01 +PAI_FORTRAN = 0x02 +PAI_ALIGNED = 0x100 +PAI_NOTSWAPPED = 0x200 +PAI_WRITEABLE = 0x400 +PAI_ARR_HAS_DESCR = 0x800 + + +class ArrayInterface(object): + def __init__(self, arr): + try: + self._cobj = arr.__array_struct__ + except AttributeError: + raise TypeError("The array object lacks an array structure") + if not self._cobj: + raise TypeError("The array object has a NULL array structure value") + try: + vp = PyCObject_AsVoidPtr(self._cobj) + except TypeError: + if PyCapsule_IsValid(self._cobj, None): + vp = PyCapsule_GetPointer(self._cobj, None) + else: + raise TypeError("The array object has an invalid array structure") + self.desc = PyCapsule_GetContext(self._cobj) + else: + self.desc = PyCObject_GetDesc(self._cobj) + self._inter = cast(vp, PAI_Ptr)[0] + + def __getattr__(self, name): + if name == "typekind": + return self._inter.typekind.decode("latin-1") + return getattr(self._inter, name) + + def __str__(self): + if isinstance(self.desc, tuple): + ver = self.desc[0] + else: + ver = "N/A" + return ( + "nd: %i\n" + "typekind: %s\n" + "itemsize: %i\n" + "flags: %s\n" + "shape: %s\n" + "strides: %s\n" + "ver: %s\n" + % ( + self.nd, + self.typekind, + self.itemsize, + format_flags(self.flags), + format_shape(self.nd, self.shape), + format_strides(self.nd, self.strides), + ver, + ) + ) + + +def format_flags(flags): + names = [] + for flag, name in [ + (PAI_CONTIGUOUS, "CONTIGUOUS"), + (PAI_FORTRAN, "FORTRAN"), + (PAI_ALIGNED, "ALIGNED"), + (PAI_NOTSWAPPED, "NOTSWAPPED"), + (PAI_WRITEABLE, "WRITEABLE"), + (PAI_ARR_HAS_DESCR, "ARR_HAS_DESCR"), + ]: + if flag & flags: + names.append(name) + return ", ".join(names) + + +def format_shape(nd, shape): + return ", ".join([str(shape[i]) for i in range(nd)]) + + +def format_strides(nd, strides): + return ", ".join([str(strides[i]) for i in range(nd)]) + + +class Exporter(object): + def __init__( + self, shape, typekind=None, itemsize=None, strides=None, descr=None, flags=None + ): + if typekind is None: + typekind = "u" + if itemsize is None: + itemsize = 1 + if flags is None: + flags = PAI_WRITEABLE | PAI_ALIGNED | PAI_NOTSWAPPED + if descr is not None: + flags |= PAI_ARR_HAS_DESCR + if len(typekind) != 1: + raise ValueError("Argument 'typekind' must be length 1 string") + nd = len(shape) + self.typekind = typekind + self.itemsize = itemsize + self.nd = nd + self.shape = tuple(shape) + self._shape = (c_ssize_t * self.nd)(*self.shape) + if strides is None: + self._strides = (c_ssize_t * self.nd)() + self._strides[self.nd - 1] = self.itemsize + for i in range(self.nd - 1, 0, -1): + self._strides[i - 1] = self.shape[i] * self._strides[i] + strides = tuple(self._strides) + self.strides = strides + elif len(strides) == nd: + self.strides = tuple(strides) + self._strides = (c_ssize_t * self.nd)(*self.strides) + else: + raise ValueError("Mismatch in length of strides and shape") + self.descr = descr + if self.is_contiguous("C"): + flags |= PAI_CONTIGUOUS + if self.is_contiguous("F"): + flags |= PAI_FORTRAN + self.flags = flags + sz = max(shape[i] * strides[i] for i in range(nd)) + self._data = (c_ubyte * sz)() + self.data = addressof(self._data) + self._inter = PyArrayInterface( + 2, + nd, + typekind.encode("latin_1"), + itemsize, + flags, + self._shape, + self._strides, + self.data, + descr, + ) + self.len = itemsize + for i in range(nd): + self.len *= self.shape[i] + + __array_struct__ = property(lambda self: capsule_new(self._inter)) + + def is_contiguous(self, fortran): + if fortran in "CA": + if self.strides[-1] == self.itemsize: + for i in range(self.nd - 1, 0, -1): + if self.strides[i - 1] != self.shape[i] * self.strides[i]: + break + else: + return True + if fortran in "FA": + if self.strides[0] == self.itemsize: + for i in range(0, self.nd - 1): + if self.strides[i + 1] != self.shape[i] * self.strides[i]: + break + else: + return True + return False + + +class Array(Exporter): + _ctypes = { + ("u", 1): c_uint8, + ("u", 2): c_uint16, + ("u", 4): c_uint32, + ("u", 8): c_uint64, + ("i", 1): c_int8, + ("i", 2): c_int16, + ("i", 4): c_int32, + ("i", 8): c_int64, + } + + def __init__(self, *args, **kwds): + super(Array, self).__init__(*args, **kwds) + try: + if self.flags & PAI_NOTSWAPPED: + ct = self._ctypes[self.typekind, self.itemsize] + elif c_int.__ctype_le__ is c_int: + ct = self._ctypes[self.typekind, self.itemsize].__ctype_be__ + else: + ct = self._ctypes[self.typekind, self.itemsize].__ctype_le__ + except KeyError: + ct = c_uint8 * self.itemsize + self._ctype = ct + self._ctype_p = POINTER(ct) + + def __getitem__(self, key): + return cast(self._addr_at(key), self._ctype_p)[0] + + def __setitem__(self, key, value): + cast(self._addr_at(key), self._ctype_p)[0] = value + + def _addr_at(self, key): + if not isinstance(key, tuple): + key = (key,) + if len(key) != self.nd: + raise ValueError("wrong number of indexes") + for i in range(self.nd): + if not (0 <= key[i] < self.shape[i]): + raise IndexError("index {} out of range".format(i)) + return self.data + sum(i * s for i, s in zip(key, self.strides)) + + +class ExporterTest(unittest.TestCase): + def test_strides(self): + self.check_args(0, (10,), "u", (2,), 20, 20, 2) + self.check_args(0, (5, 3), "u", (6, 2), 30, 30, 2) + self.check_args(0, (7, 3, 5), "u", (30, 10, 2), 210, 210, 2) + self.check_args(0, (13, 5, 11, 3), "u", (330, 66, 6, 2), 4290, 4290, 2) + self.check_args(3, (7, 3, 5), "i", (2, 14, 42), 210, 210, 2) + self.check_args(3, (7, 3, 5), "x", (2, 16, 48), 210, 240, 2) + self.check_args(3, (13, 5, 11, 3), "%", (440, 88, 8, 2), 4290, 5720, 2) + self.check_args(3, (7, 5), "-", (15, 3), 105, 105, 3) + self.check_args(3, (7, 5), "*", (3, 21), 105, 105, 3) + self.check_args(3, (7, 5), " ", (3, 24), 105, 120, 3) + + def test_is_contiguous(self): + a = Exporter((10,), itemsize=2) + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) + a = Exporter((10, 4), itemsize=2) + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("F")) + a = Exporter((13, 5, 11, 3), itemsize=2, strides=(330, 66, 6, 2)) + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("F")) + a = Exporter((10, 4), itemsize=2, strides=(2, 20)) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("C")) + a = Exporter((13, 5, 11, 3), itemsize=2, strides=(2, 26, 130, 1430)) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("C")) + a = Exporter((2, 11, 6, 4), itemsize=2, strides=(576, 48, 8, 2)) + self.assertFalse(a.is_contiguous("A")) + a = Exporter((2, 11, 6, 4), itemsize=2, strides=(2, 4, 48, 288)) + self.assertFalse(a.is_contiguous("A")) + a = Exporter((3, 2, 2), itemsize=2, strides=(16, 8, 4)) + self.assertFalse(a.is_contiguous("A")) + a = Exporter((3, 2, 2), itemsize=2, strides=(4, 12, 24)) + self.assertFalse(a.is_contiguous("A")) + + def check_args( + self, call_flags, shape, typekind, strides, length, bufsize, itemsize, offset=0 + ): + if call_flags & 1: + typekind_arg = typekind + else: + typekind_arg = None + if call_flags & 2: + strides_arg = strides + else: + strides_arg = None + a = Exporter(shape, itemsize=itemsize, strides=strides_arg) + self.assertEqual(sizeof(a._data), bufsize) + self.assertEqual(a.data, ctypes.addressof(a._data) + offset) + m = ArrayInterface(a) + self.assertEqual(m.data, a.data) + self.assertEqual(m.itemsize, itemsize) + self.assertEqual(tuple(m.shape[0 : m.nd]), shape) + self.assertEqual(tuple(m.strides[0 : m.nd]), strides) + + +class ArrayTest(unittest.TestCase): + def __init__(self, *args, **kwds): + unittest.TestCase.__init__(self, *args, **kwds) + self.a = Array((20, 15), "i", 4) + + def setUp(self): + # Every test starts with a zeroed array. + memset(self.a.data, 0, sizeof(self.a._data)) + + def test__addr_at(self): + a = self.a + self.assertEqual(a._addr_at((0, 0)), a.data) + self.assertEqual(a._addr_at((0, 1)), a.data + 4) + self.assertEqual(a._addr_at((1, 0)), a.data + 60) + self.assertEqual(a._addr_at((1, 1)), a.data + 64) + + def test_indices(self): + a = self.a + self.assertEqual(a[0, 0], 0) + self.assertEqual(a[19, 0], 0) + self.assertEqual(a[0, 14], 0) + self.assertEqual(a[19, 14], 0) + self.assertEqual(a[5, 8], 0) + a[0, 0] = 12 + a[5, 8] = 99 + self.assertEqual(a[0, 0], 12) + self.assertEqual(a[5, 8], 99) + self.assertRaises(IndexError, a.__getitem__, (-1, 0)) + self.assertRaises(IndexError, a.__getitem__, (0, -1)) + self.assertRaises(IndexError, a.__getitem__, (20, 0)) + self.assertRaises(IndexError, a.__getitem__, (0, 15)) + self.assertRaises(ValueError, a.__getitem__, 0) + self.assertRaises(ValueError, a.__getitem__, (0, 0, 0)) + a = Array((3,), "i", 4) + a[1] = 333 + self.assertEqual(a[1], 333) + + def test_typekind(self): + a = Array((1,), "i", 4) + self.assertTrue(a._ctype is c_int32) + self.assertTrue(a._ctype_p is POINTER(c_int32)) + a = Array((1,), "u", 4) + self.assertTrue(a._ctype is c_uint32) + self.assertTrue(a._ctype_p is POINTER(c_uint32)) + a = Array((1,), "f", 4) # float types unsupported: size system dependent + ct = a._ctype + self.assertTrue(issubclass(ct, ctypes.Array)) + self.assertEqual(sizeof(ct), 4) + + def test_itemsize(self): + for size in [1, 2, 4, 8]: + a = Array((1,), "i", size) + ct = a._ctype + self.assertTrue(issubclass(ct, ctypes._SimpleCData)) + self.assertEqual(sizeof(ct), size) + + def test_oddball_itemsize(self): + for size in [3, 5, 6, 7, 9]: + a = Array((1,), "i", size) + ct = a._ctype + self.assertTrue(issubclass(ct, ctypes.Array)) + self.assertEqual(sizeof(ct), size) + + def test_byteswapped(self): + a = Array((1,), "u", 4, flags=(PAI_ALIGNED | PAI_WRITEABLE)) + ct = a._ctype + self.assertTrue(ct is not c_uint32) + if sys.byteorder == "little": + self.assertTrue(ct is c_uint32.__ctype_be__) + else: + self.assertTrue(ct is c_uint32.__ctype_le__) + i = 0xA0B0C0D + n = c_uint32(i) + a[0] = i + self.assertEqual(a[0], i) + self.assertEqual(a._data[0:4], cast(addressof(n), POINTER(c_uint8))[3:-1:-1]) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/async_sub.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/async_sub.py new file mode 100644 index 0000000..f760669 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/async_sub.py @@ -0,0 +1,313 @@ +################################################################################ +""" + +Modification of http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554 + +""" + +#################################### IMPORTS ################################### + +import os +import platform +import subprocess +import errno +import time +import sys +import unittest +import tempfile + + +def geterror(): + return sys.exc_info()[1] + + +if sys.version_info >= (3,): + null_byte = "\x00".encode("ascii") +else: + null_byte = "\x00" + +if platform.system() == "Windows": + if sys.version_info >= (3,): + # Test date should be in ascii. + def encode(s): + return s.encode("ascii") + + def decode(b): + return b.decode("ascii") + + else: + # Strings only; do nothing + def encode(s): + return s + + def decode(b): + return b + + try: + import ctypes + from ctypes.wintypes import DWORD + + kernel32 = ctypes.windll.kernel32 + TerminateProcess = ctypes.windll.kernel32.TerminateProcess + + def WriteFile(handle, data, ol=None): + c_written = DWORD() + success = ctypes.windll.kernel32.WriteFile( + handle, + ctypes.create_string_buffer(encode(data)), + len(data), + ctypes.byref(c_written), + ol, + ) + return ctypes.windll.kernel32.GetLastError(), c_written.value + + def ReadFile(handle, desired_bytes, ol=None): + c_read = DWORD() + buffer = ctypes.create_string_buffer(desired_bytes + 1) + success = ctypes.windll.kernel32.ReadFile( + handle, buffer, desired_bytes, ctypes.byref(c_read), ol + ) + buffer[c_read.value] = null_byte + return ctypes.windll.kernel32.GetLastError(), decode(buffer.value) + + def PeekNamedPipe(handle, desired_bytes): + c_avail = DWORD() + c_message = DWORD() + if desired_bytes > 0: + c_read = DWORD() + buffer = ctypes.create_string_buffer(desired_bytes + 1) + success = ctypes.windll.kernel32.PeekNamedPipe( + handle, + buffer, + desired_bytes, + ctypes.byref(c_read), + ctypes.byref(c_avail), + ctypes.byref(c_message), + ) + buffer[c_read.value] = null_byte + return decode(buffer.value), c_avail.value, c_message.value + else: + success = ctypes.windll.kernel32.PeekNamedPipe( + handle, + None, + desired_bytes, + None, + ctypes.byref(c_avail), + ctypes.byref(c_message), + ) + return "", c_avail.value, c_message.value + + except ImportError: + from win32file import ReadFile, WriteFile + from win32pipe import PeekNamedPipe + from win32api import TerminateProcess + import msvcrt + +else: + from signal import SIGINT, SIGTERM, SIGKILL + import select + import fcntl + +################################### CONSTANTS ################################## + +PIPE = subprocess.PIPE + +################################################################################ + + +class Popen(subprocess.Popen): + def recv(self, maxsize=None): + return self._recv("stdout", maxsize) + + def recv_err(self, maxsize=None): + return self._recv("stderr", maxsize) + + def send_recv(self, input="", maxsize=None): + return self.send(input), self.recv(maxsize), self.recv_err(maxsize) + + def read_async(self, wait=0.1, e=1, tr=5, stderr=0): + if tr < 1: + tr = 1 + x = time.time() + wait + y = [] + r = "" + pr = self.recv + if stderr: + pr = self.recv_err + while time.time() < x or r: + r = pr() + if r is None: + if e: + raise Exception("Other end disconnected!") + else: + break + elif r: + y.append(r) + else: + time.sleep(max((x - time.time()) / tr, 0)) + return "".join(y) + + def send_all(self, data): + while len(data): + sent = self.send(data) + if sent is None: + raise Exception("Other end disconnected!") + data = buffer(data, sent) + + def get_conn_maxsize(self, which, maxsize): + if maxsize is None: + maxsize = 1024 + elif maxsize < 1: + maxsize = 1 + return getattr(self, which), maxsize + + def _close(self, which): + getattr(self, which).close() + setattr(self, which, None) + + if platform.system() == "Windows": + + def kill(self): + # Recipes + # http://me.in-berlin.de/doc/python/faq/windows.html#how-do-i-emulate-os-kill-in-windows + # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/347462 + + """kill function for Win32""" + TerminateProcess(int(self._handle), 0) # returns None + + def send(self, input): + if not self.stdin: + return None + + try: + x = msvcrt.get_osfhandle(self.stdin.fileno()) + (errCode, written) = WriteFile(x, input) + except ValueError: + return self._close("stdin") + except (subprocess.pywintypes.error, Exception): + if geterror()[0] in (109, errno.ESHUTDOWN): + return self._close("stdin") + raise + + return written + + def _recv(self, which, maxsize): + conn, maxsize = self.get_conn_maxsize(which, maxsize) + if conn is None: + return None + + try: + x = msvcrt.get_osfhandle(conn.fileno()) + (read, nAvail, nMessage) = PeekNamedPipe(x, 0) + if maxsize < nAvail: + nAvail = maxsize + if nAvail > 0: + (errCode, read) = ReadFile(x, nAvail, None) + except ValueError: + return self._close(which) + except (subprocess.pywintypes.error, Exception): + if geterror()[0] in (109, errno.ESHUTDOWN): + return self._close(which) + raise + + if self.universal_newlines: + # Translate newlines. For Python 3.x assume read is text. + # If bytes then another solution is needed. + read = read.replace("\r\n", "\n").replace("\r", "\n") + return read + + else: + + def kill(self): + for i, sig in enumerate([SIGTERM, SIGKILL] * 2): + if i % 2 == 0: + os.kill(self.pid, sig) + time.sleep((i * (i % 2) / 5.0) + 0.01) + + killed_pid, stat = os.waitpid(self.pid, os.WNOHANG) + if killed_pid != 0: + return + + def send(self, input): + if not self.stdin: + return None + + if not select.select([], [self.stdin], [], 0)[1]: + return 0 + + try: + written = os.write(self.stdin.fileno(), input) + except OSError: + if geterror()[0] == errno.EPIPE: # broken pipe + return self._close("stdin") + raise + + return written + + def _recv(self, which, maxsize): + conn, maxsize = self.get_conn_maxsize(which, maxsize) + if conn is None: + return None + + if not select.select([conn], [], [], 0)[0]: + return "" + + r = conn.read(maxsize) + if not r: + return self._close(which) + + if self.universal_newlines: + r = r.replace("\r\n", "\n").replace("\r", "\n") + return r + + +################################################################################ + + +def proc_in_time_or_kill(cmd, time_out, wd=None, env=None): + proc = Popen( + cmd, + cwd=wd, + env=env, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=1, + ) + + ret_code = None + response = [] + + t = time.time() + while ret_code is None and ((time.time() - t) < time_out): + ret_code = proc.poll() + response += [proc.read_async(wait=0.1, e=0)] + + if ret_code is None: + ret_code = '"Process timed out (time_out = %s secs) ' % time_out + try: + proc.kill() + ret_code += 'and was successfully terminated"' + except Exception: + ret_code += 'and termination failed (exception: %s)"' % (geterror(),) + + return ret_code, "".join(response) + + +################################################################################ + + +class AsyncTest(unittest.TestCase): + def test_proc_in_time_or_kill(self): + ret_code, response = proc_in_time_or_kill( + [sys.executable, "-c", "while 1: pass"], time_out=1 + ) + + self.assertIn("rocess timed out", ret_code) + self.assertIn("successfully terminated", ret_code) + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/buftools.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/buftools.py new file mode 100644 index 0000000..b8d3ac6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/buftools.py @@ -0,0 +1,613 @@ +"""Module pygame.tests.test_utils.array + +Export the Exporter and Importer classes. + +Class Exporter has configurable shape and strides. Exporter objects +provide a convient target for unit tests on Pygame objects and functions that +import a new buffer interface. + +Class Importer imports a buffer interface with the given PyBUF_* flags. +It returns NULL Py_buffer fields as None. The shape, strides, and suboffsets +arrays are returned as tuples of ints. All Py_buffer field properties are +read-only. This class is useful in comparing exported buffer interfaces +with the actual request. The simular Python builtin memoryview currently +does not support configurable PyBUF_* flags. + +This module contains its own unit tests. When Pygame is installed, these tests +can be run with the following command line statement: + +python -m pygame.tests.test_utils.array + +""" +import pygame + +if not pygame.HAVE_NEWBUF: + emsg = "This Pygame build does not support the new buffer protocol" + raise ImportError(emsg) +import pygame.newbuffer +from pygame.newbuffer import ( + PyBUF_SIMPLE, + PyBUF_FORMAT, + PyBUF_ND, + PyBUF_WRITABLE, + PyBUF_STRIDES, + PyBUF_C_CONTIGUOUS, + PyBUF_F_CONTIGUOUS, + PyBUF_ANY_CONTIGUOUS, + PyBUF_INDIRECT, + PyBUF_STRIDED, + PyBUF_STRIDED_RO, + PyBUF_RECORDS, + PyBUF_RECORDS_RO, + PyBUF_FULL, + PyBUF_FULL_RO, + PyBUF_CONTIG, + PyBUF_CONTIG_RO, +) + +import unittest +import sys +import ctypes +import operator + +try: + reduce +except NameError: + from functools import reduce + +__all__ = ["Exporter", "Importer"] + +try: + ctypes.c_ssize_t +except AttributeError: + void_p_sz = ctypes.sizeof(ctypes.c_void_p) + if ctypes.sizeof(ctypes.c_short) == void_p_sz: + ctypes.c_ssize_t = ctypes.c_short + elif ctypes.sizeof(ctypes.c_int) == void_p_sz: + ctypes.c_ssize_t = ctypes.c_int + elif ctypes.sizeof(ctypes.c_long) == void_p_sz: + ctypes.c_ssize_t = ctypes.c_long + elif ctypes.sizeof(ctypes.c_longlong) == void_p_sz: + ctypes.c_ssize_t = ctypes.c_longlong + else: + raise RuntimeError("Cannot set c_ssize_t: sizeof(void *) is %i" % void_p_sz) + + +def _prop_get(fn): + return property(fn) + + +class Exporter(pygame.newbuffer.BufferMixin): + """An object that exports a multi-dimension new buffer interface + + The only array operation this type supports is to export a buffer. + """ + + prefixes = { + "@": "", + "=": "=", + "<": "=", + ">": "=", + "!": "=", + "2": "2", + "3": "3", + "4": "4", + "5": "5", + "6": "6", + "7": "7", + "8": "8", + "9": "9", + } + types = { + "c": ctypes.c_char, + "b": ctypes.c_byte, + "B": ctypes.c_ubyte, + "=c": ctypes.c_int8, + "=b": ctypes.c_int8, + "=B": ctypes.c_uint8, + "?": ctypes.c_bool, + "=?": ctypes.c_int8, + "h": ctypes.c_short, + "H": ctypes.c_ushort, + "=h": ctypes.c_int16, + "=H": ctypes.c_uint16, + "i": ctypes.c_int, + "I": ctypes.c_uint, + "=i": ctypes.c_int32, + "=I": ctypes.c_uint32, + "l": ctypes.c_long, + "L": ctypes.c_ulong, + "=l": ctypes.c_int32, + "=L": ctypes.c_uint32, + "q": ctypes.c_longlong, + "Q": ctypes.c_ulonglong, + "=q": ctypes.c_int64, + "=Q": ctypes.c_uint64, + "f": ctypes.c_float, + "d": ctypes.c_double, + "P": ctypes.c_void_p, + "x": ctypes.c_ubyte * 1, + "2x": ctypes.c_ubyte * 2, + "3x": ctypes.c_ubyte * 3, + "4x": ctypes.c_ubyte * 4, + "5x": ctypes.c_ubyte * 5, + "6x": ctypes.c_ubyte * 6, + "7x": ctypes.c_ubyte * 7, + "8x": ctypes.c_ubyte * 8, + "9x": ctypes.c_ubyte * 9, + } + + def __init__(self, shape, format=None, strides=None, readonly=None, itemsize=None): + if format is None: + format = "B" + if readonly is None: + readonly = False + prefix = "" + typecode = "" + i = 0 + if i < len(format): + try: + prefix = self.prefixes[format[i]] + i += 1 + except LookupError: + pass + if i < len(format) and format[i] == "1": + i += 1 + if i == len(format) - 1: + typecode = format[i] + if itemsize is None: + try: + itemsize = ctypes.sizeof(self.types[prefix + typecode]) + except KeyError: + raise ValueError("Unknown item format '" + format + "'") + self.readonly = bool(readonly) + self.format = format + self._format = ctypes.create_string_buffer(format.encode("latin_1")) + self.ndim = len(shape) + self.itemsize = itemsize + self.len = reduce(operator.mul, shape, 1) * self.itemsize + self.shape = tuple(shape) + self._shape = (ctypes.c_ssize_t * self.ndim)(*self.shape) + if strides is None: + self._strides = (ctypes.c_ssize_t * self.ndim)() + self._strides[self.ndim - 1] = itemsize + for i in range(self.ndim - 1, 0, -1): + self._strides[i - 1] = self.shape[i] * self._strides[i] + self.strides = tuple(self._strides) + elif len(strides) == self.ndim: + self.strides = tuple(strides) + self._strides = (ctypes.c_ssize_t * self.ndim)(*self.strides) + else: + raise ValueError("Mismatch in length of strides and shape") + buflen = max(d * abs(s) for d, s in zip(self.shape, self.strides)) + self.buflen = buflen + self._buf = (ctypes.c_ubyte * buflen)() + offset = sum( + (d - 1) * abs(s) for d, s in zip(self.shape, self.strides) if s < 0 + ) + self.buf = ctypes.addressof(self._buf) + offset + + def buffer_info(self): + return (addressof(self.buffer), self.shape[0]) + + def tobytes(self): + return cast(self.buffer, POINTER(c_char))[0 : self._len] + + def __len__(self): + return self.shape[0] + + def _get_buffer(self, view, flags): + from ctypes import addressof + + if (flags & PyBUF_WRITABLE) == PyBUF_WRITABLE and self.readonly: + raise BufferError("buffer is read-only") + if ( + flags & PyBUF_C_CONTIGUOUS + ) == PyBUF_C_CONTIGUOUS and not self.is_contiguous("C"): + raise BufferError("data is not C contiguous") + if ( + flags & PyBUF_F_CONTIGUOUS + ) == PyBUF_F_CONTIGUOUS and not self.is_contiguous("F"): + raise BufferError("data is not F contiguous") + if ( + flags & PyBUF_ANY_CONTIGUOUS + ) == PyBUF_ANY_CONTIGUOUS and not self.is_contiguous("A"): + raise BufferError("data is not contiguous") + view.buf = self.buf + view.readonly = self.readonly + view.len = self.len + if flags | PyBUF_WRITABLE == PyBUF_WRITABLE: + view.ndim = 0 + else: + view.ndim = self.ndim + view.itemsize = self.itemsize + if (flags & PyBUF_FORMAT) == PyBUF_FORMAT: + view.format = addressof(self._format) + else: + view.format = None + if (flags & PyBUF_ND) == PyBUF_ND: + view.shape = addressof(self._shape) + elif self.is_contiguous("C"): + view.shape = None + else: + raise BufferError( + "shape required for {} dimensional data".format(self.ndim) + ) + if (flags & PyBUF_STRIDES) == PyBUF_STRIDES: + view.strides = ctypes.addressof(self._strides) + elif view.shape is None or self.is_contiguous("C"): + view.strides = None + else: + raise BufferError("strides required for none C contiguous data") + view.suboffsets = None + view.internal = None + view.obj = self + + def is_contiguous(self, fortran): + if fortran in "CA": + if self.strides[-1] == self.itemsize: + for i in range(self.ndim - 1, 0, -1): + if self.strides[i - 1] != self.shape[i] * self.strides[i]: + break + else: + return True + if fortran in "FA": + if self.strides[0] == self.itemsize: + for i in range(0, self.ndim - 1): + if self.strides[i + 1] != self.shape[i] * self.strides[i]: + break + else: + return True + return False + + +class Importer(object): + """An object that imports a new buffer interface + + The fields of the Py_buffer C struct are exposed by identically + named Importer read-only properties. + """ + + def __init__(self, obj, flags): + self._view = pygame.newbuffer.Py_buffer() + self._view.get_buffer(obj, flags) + + @property + def obj(self): + """return object or None for NULL field""" + return self._view.obj + + @property + def buf(self): + """return int or None for NULL field""" + return self._view.buf + + @property + def len(self): + """return int""" + return self._view.len + + @property + def readonly(self): + """return bool""" + return self._view.readonly + + @property + def format(self): + """return bytes or None for NULL field""" + format_addr = self._view.format + if format_addr is None: + return None + return ctypes.cast(format_addr, ctypes.c_char_p).value.decode("ascii") + + @property + def itemsize(self): + """return int""" + return self._view.itemsize + + @property + def ndim(self): + """return int""" + return self._view.ndim + + @property + def shape(self): + """return int tuple or None for NULL field""" + return self._to_ssize_tuple(self._view.shape) + + @property + def strides(self): + """return int tuple or None for NULL field""" + return self._to_ssize_tuple(self._view.strides) + + @property + def suboffsets(self): + """return int tuple or None for NULL field""" + return self._to_ssize_tuple(self._view.suboffsets) + + @property + def internal(self): + """return int or None for NULL field""" + return self._view.internal + + def _to_ssize_tuple(self, addr): + from ctypes import cast, POINTER, c_ssize_t + + if addr is None: + return None + return tuple(cast(addr, POINTER(c_ssize_t))[0 : self._view.ndim]) + + +class ExporterTest(unittest.TestCase): + """Class Exporter unit tests""" + + def test_formats(self): + char_sz = ctypes.sizeof(ctypes.c_char) + short_sz = ctypes.sizeof(ctypes.c_short) + int_sz = ctypes.sizeof(ctypes.c_int) + long_sz = ctypes.sizeof(ctypes.c_long) + longlong_sz = ctypes.sizeof(ctypes.c_longlong) + float_sz = ctypes.sizeof(ctypes.c_float) + double_sz = ctypes.sizeof(ctypes.c_double) + voidp_sz = ctypes.sizeof(ctypes.c_void_p) + bool_sz = ctypes.sizeof(ctypes.c_bool) + + self.check_args(0, (1,), "B", (1,), 1, 1, 1) + self.check_args(1, (1,), "b", (1,), 1, 1, 1) + self.check_args(1, (1,), "B", (1,), 1, 1, 1) + self.check_args(1, (1,), "c", (char_sz,), char_sz, char_sz, char_sz) + self.check_args(1, (1,), "h", (short_sz,), short_sz, short_sz, short_sz) + self.check_args(1, (1,), "H", (short_sz,), short_sz, short_sz, short_sz) + self.check_args(1, (1,), "i", (int_sz,), int_sz, int_sz, int_sz) + self.check_args(1, (1,), "I", (int_sz,), int_sz, int_sz, int_sz) + self.check_args(1, (1,), "l", (long_sz,), long_sz, long_sz, long_sz) + self.check_args(1, (1,), "L", (long_sz,), long_sz, long_sz, long_sz) + self.check_args( + 1, (1,), "q", (longlong_sz,), longlong_sz, longlong_sz, longlong_sz + ) + self.check_args( + 1, (1,), "Q", (longlong_sz,), longlong_sz, longlong_sz, longlong_sz + ) + self.check_args(1, (1,), "f", (float_sz,), float_sz, float_sz, float_sz) + self.check_args(1, (1,), "d", (double_sz,), double_sz, double_sz, double_sz) + self.check_args(1, (1,), "x", (1,), 1, 1, 1) + self.check_args(1, (1,), "P", (voidp_sz,), voidp_sz, voidp_sz, voidp_sz) + self.check_args(1, (1,), "?", (bool_sz,), bool_sz, bool_sz, bool_sz) + self.check_args(1, (1,), "@b", (1,), 1, 1, 1) + self.check_args(1, (1,), "@B", (1,), 1, 1, 1) + self.check_args(1, (1,), "@c", (char_sz,), char_sz, char_sz, char_sz) + self.check_args(1, (1,), "@h", (short_sz,), short_sz, short_sz, short_sz) + self.check_args(1, (1,), "@H", (short_sz,), short_sz, short_sz, short_sz) + self.check_args(1, (1,), "@i", (int_sz,), int_sz, int_sz, int_sz) + self.check_args(1, (1,), "@I", (int_sz,), int_sz, int_sz, int_sz) + self.check_args(1, (1,), "@l", (long_sz,), long_sz, long_sz, long_sz) + self.check_args(1, (1,), "@L", (long_sz,), long_sz, long_sz, long_sz) + self.check_args( + 1, (1,), "@q", (longlong_sz,), longlong_sz, longlong_sz, longlong_sz + ) + self.check_args( + 1, (1,), "@Q", (longlong_sz,), longlong_sz, longlong_sz, longlong_sz + ) + self.check_args(1, (1,), "@f", (float_sz,), float_sz, float_sz, float_sz) + self.check_args(1, (1,), "@d", (double_sz,), double_sz, double_sz, double_sz) + self.check_args(1, (1,), "@?", (bool_sz,), bool_sz, bool_sz, bool_sz) + self.check_args(1, (1,), "=b", (1,), 1, 1, 1) + self.check_args(1, (1,), "=B", (1,), 1, 1, 1) + self.check_args(1, (1,), "=c", (1,), 1, 1, 1) + self.check_args(1, (1,), "=h", (2,), 2, 2, 2) + self.check_args(1, (1,), "=H", (2,), 2, 2, 2) + self.check_args(1, (1,), "=i", (4,), 4, 4, 4) + self.check_args(1, (1,), "=I", (4,), 4, 4, 4) + self.check_args(1, (1,), "=l", (4,), 4, 4, 4) + self.check_args(1, (1,), "=L", (4,), 4, 4, 4) + self.check_args(1, (1,), "=q", (8,), 8, 8, 8) + self.check_args(1, (1,), "=Q", (8,), 8, 8, 8) + self.check_args(1, (1,), "=?", (1,), 1, 1, 1) + self.check_args(1, (1,), "h", (2,), 2, 2, 2) + self.check_args(1, (1,), "!h", (2,), 2, 2, 2) + self.check_args(1, (1,), "q", (8,), 8, 8, 8) + self.check_args(1, (1,), "!q", (8,), 8, 8, 8) + self.check_args(1, (1,), "1x", (1,), 1, 1, 1) + self.check_args(1, (1,), "2x", (2,), 2, 2, 2) + self.check_args(1, (1,), "3x", (3,), 3, 3, 3) + self.check_args(1, (1,), "4x", (4,), 4, 4, 4) + self.check_args(1, (1,), "5x", (5,), 5, 5, 5) + self.check_args(1, (1,), "6x", (6,), 6, 6, 6) + self.check_args(1, (1,), "7x", (7,), 7, 7, 7) + self.check_args(1, (1,), "8x", (8,), 8, 8, 8) + self.check_args(1, (1,), "9x", (9,), 9, 9, 9) + self.check_args(1, (1,), "1h", (2,), 2, 2, 2) + self.check_args(1, (1,), "=1h", (2,), 2, 2, 2) + self.assertRaises(ValueError, Exporter, (2, 1), "") + self.assertRaises(ValueError, Exporter, (2, 1), "W") + self.assertRaises(ValueError, Exporter, (2, 1), "^Q") + self.assertRaises(ValueError, Exporter, (2, 1), "=W") + self.assertRaises(ValueError, Exporter, (2, 1), "=f") + self.assertRaises(ValueError, Exporter, (2, 1), "=d") + self.assertRaises(ValueError, Exporter, (2, 1), "f") + self.assertRaises(ValueError, Exporter, (2, 1), ">d") + self.assertRaises(ValueError, Exporter, (2, 1), "!f") + self.assertRaises(ValueError, Exporter, (2, 1), "!d") + self.assertRaises(ValueError, Exporter, (2, 1), "0x") + self.assertRaises(ValueError, Exporter, (2, 1), "11x") + self.assertRaises(ValueError, Exporter, (2, 1), "BB") + + def test_strides(self): + self.check_args(1, (10,), "=h", (2,), 20, 20, 2) + self.check_args(1, (5, 3), "=h", (6, 2), 30, 30, 2) + self.check_args(1, (7, 3, 5), "=h", (30, 10, 2), 210, 210, 2) + self.check_args(1, (13, 5, 11, 3), "=h", (330, 66, 6, 2), 4290, 4290, 2) + self.check_args(3, (7, 3, 5), "=h", (2, 14, 42), 210, 210, 2) + self.check_args(3, (7, 3, 5), "=h", (2, 16, 48), 210, 240, 2) + self.check_args(3, (13, 5, 11, 3), "=h", (440, 88, 8, 2), 4290, 5720, 2) + self.check_args(3, (7, 5), "3x", (15, 3), 105, 105, 3) + self.check_args(3, (7, 5), "3x", (3, 21), 105, 105, 3) + self.check_args(3, (7, 5), "3x", (3, 24), 105, 120, 3) + + def test_readonly(self): + a = Exporter((2,), "h", readonly=True) + self.assertTrue(a.readonly) + b = Importer(a, PyBUF_STRIDED_RO) + self.assertRaises(BufferError, Importer, a, PyBUF_STRIDED) + b = Importer(a, PyBUF_STRIDED_RO) + + def test_is_contiguous(self): + a = Exporter((10,), "=h") + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) + a = Exporter((10, 4), "=h") + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("F")) + a = Exporter((13, 5, 11, 3), "=h", (330, 66, 6, 2)) + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("F")) + a = Exporter((10, 4), "=h", (2, 20)) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("C")) + a = Exporter((13, 5, 11, 3), "=h", (2, 26, 130, 1430)) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("C")) + a = Exporter((2, 11, 6, 4), "=h", (576, 48, 8, 2)) + self.assertFalse(a.is_contiguous("A")) + a = Exporter((2, 11, 6, 4), "=h", (2, 4, 48, 288)) + self.assertFalse(a.is_contiguous("A")) + a = Exporter((3, 2, 2), "=h", (16, 8, 4)) + self.assertFalse(a.is_contiguous("A")) + a = Exporter((3, 2, 2), "=h", (4, 12, 24)) + self.assertFalse(a.is_contiguous("A")) + + def test_PyBUF_flags(self): + a = Exporter((10, 2), "d") + b = Importer(a, PyBUF_SIMPLE) + self.assertTrue(b.obj is a) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.len) + self.assertEqual(b.itemsize, a.itemsize) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertTrue(b.internal is None) + self.assertFalse(b.readonly) + b = Importer(a, PyBUF_WRITABLE) + self.assertTrue(b.obj is a) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.len) + self.assertEqual(b.itemsize, a.itemsize) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertTrue(b.internal is None) + self.assertFalse(b.readonly) + b = Importer(a, PyBUF_ND) + self.assertTrue(b.obj is a) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.len) + self.assertEqual(b.itemsize, a.itemsize) + self.assertEqual(b.shape, a.shape) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertTrue(b.internal is None) + self.assertFalse(b.readonly) + a = Exporter((5, 10), "=h", (24, 2)) + b = Importer(a, PyBUF_STRIDES) + self.assertTrue(b.obj is a) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.len) + self.assertEqual(b.itemsize, a.itemsize) + self.assertEqual(b.shape, a.shape) + self.assertEqual(b.strides, a.strides) + self.assertTrue(b.suboffsets is None) + self.assertTrue(b.internal is None) + self.assertFalse(b.readonly) + b = Importer(a, PyBUF_FULL) + self.assertTrue(b.obj is a) + self.assertEqual(b.format, "=h") + self.assertEqual(b.len, a.len) + self.assertEqual(b.itemsize, a.itemsize) + self.assertEqual(b.shape, a.shape) + self.assertEqual(b.strides, a.strides) + self.assertTrue(b.suboffsets is None) + self.assertTrue(b.internal is None) + self.assertFalse(b.readonly) + self.assertRaises(BufferError, Importer, a, PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, a, PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, a, PyBUF_ND) + self.assertRaises(BufferError, Importer, a, PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, PyBUF_ANY_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, PyBUF_CONTIG) + + def test_negative_strides(self): + self.check_args(3, (3, 5, 4), "B", (20, 4, -1), 60, 60, 1, 3) + self.check_args(3, (3, 5, 3), "B", (20, 4, -1), 45, 60, 1, 2) + self.check_args(3, (3, 5, 4), "B", (20, -4, 1), 60, 60, 1, 16) + self.check_args(3, (3, 5, 4), "B", (-20, -4, -1), 60, 60, 1, 59) + self.check_args(3, (3, 5, 3), "B", (-20, -4, -1), 45, 60, 1, 58) + + def test_attributes(self): + a = Exporter((13, 5, 11, 3), "=h", (440, 88, 8, 2)) + self.assertEqual(a.ndim, 4) + self.assertEqual(a.itemsize, 2) + self.assertFalse(a.readonly) + self.assertEqual(a.shape, (13, 5, 11, 3)) + self.assertEqual(a.format, "=h") + self.assertEqual(a.strides, (440, 88, 8, 2)) + self.assertEqual(a.len, 4290) + self.assertEqual(a.buflen, 5720) + self.assertEqual(a.buf, ctypes.addressof(a._buf)) + a = Exporter((8,)) + self.assertEqual(a.ndim, 1) + self.assertEqual(a.itemsize, 1) + self.assertFalse(a.readonly) + self.assertEqual(a.shape, (8,)) + self.assertEqual(a.format, "B") + self.assertTrue(isinstance(a.strides, tuple)) + self.assertEqual(a.strides, (1,)) + self.assertEqual(a.len, 8) + self.assertEqual(a.buflen, 8) + a = Exporter([13, 5, 11, 3], "=h", [440, 88, 8, 2]) + self.assertTrue(isinstance(a.shape, tuple)) + self.assertTrue(isinstance(a.strides, tuple)) + self.assertEqual(a.shape, (13, 5, 11, 3)) + self.assertEqual(a.strides, (440, 88, 8, 2)) + + def test_itemsize(self): + exp = Exporter((4, 5), format="B", itemsize=8) + imp = Importer(exp, PyBUF_RECORDS) + self.assertEqual(imp.itemsize, 8) + self.assertEqual(imp.format, "B") + self.assertEqual(imp.strides, (40, 8)) + exp = Exporter((4, 5), format="weird", itemsize=5) + imp = Importer(exp, PyBUF_RECORDS) + self.assertEqual(imp.itemsize, 5) + self.assertEqual(imp.format, "weird") + self.assertEqual(imp.strides, (25, 5)) + + def check_args( + self, call_flags, shape, format, strides, length, bufsize, itemsize, offset=0 + ): + format_arg = format if call_flags & 1 else None + strides_arg = strides if call_flags & 2 else None + a = Exporter(shape, format_arg, strides_arg) + self.assertEqual(a.buflen, bufsize) + self.assertEqual(a.buf, ctypes.addressof(a._buf) + offset) + m = Importer(a, PyBUF_RECORDS_RO) + self.assertEqual(m.buf, a.buf) + self.assertEqual(m.len, length) + self.assertEqual(m.format, format) + self.assertEqual(m.itemsize, itemsize) + self.assertEqual(m.shape, shape) + self.assertEqual(m.strides, strides) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/endian.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/endian.py new file mode 100644 index 0000000..64ba1b3 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/endian.py @@ -0,0 +1,20 @@ +# Module pygame.tests.test_utils.endian +# +# Machine independent conversion to little-endian and big-endian Python +# integer values. + +import struct + + +def little_endian_uint32(i): + """Return the 32 bit unsigned integer little-endian representation of i""" + + s = struct.pack("I", i) + return struct.unpack("=I", s)[0] diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/png.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/png.py new file mode 100644 index 0000000..7cec9c9 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/png.py @@ -0,0 +1,4006 @@ +#!/usr/bin/env python + +# $URL: http://pypng.googlecode.com/svn/trunk/code/png.py $ +# $Rev: 228 $ + +# png.py - PNG encoder/decoder in pure Python +# +# Modified for Pygame in Oct., 2012 to work with Python 3.x. +# +# Copyright (C) 2006 Johann C. Rocholl +# Portions Copyright (C) 2009 David Jones +# And probably portions Copyright (C) 2006 Nicko van Someren +# +# Original concept by Johann C. Rocholl. +# +# LICENSE (The MIT License) +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Changelog (recent first): +# 2009-03-11 David: interlaced bit depth < 8 (writing). +# 2009-03-10 David: interlaced bit depth < 8 (reading). +# 2009-03-04 David: Flat and Boxed pixel formats. +# 2009-02-26 David: Palette support (writing). +# 2009-02-23 David: Bit-depths < 8; better PNM support. +# 2006-06-17 Nicko: Reworked into a class, faster interlacing. +# 2006-06-17 Johann: Very simple prototype PNG decoder. +# 2006-06-17 Nicko: Test suite with various image generators. +# 2006-06-17 Nicko: Alpha-channel, grey-scale, 16-bit/plane support. +# 2006-06-15 Johann: Scanline iterator interface for large input files. +# 2006-06-09 Johann: Very simple prototype PNG encoder. + +# Incorporated into Bangai-O Development Tools by drj on 2009-02-11 from +# http://trac.browsershots.org/browser/trunk/pypng/lib/png.py?rev=2885 + +# Incorporated into pypng by drj on 2009-03-12 from +# //depot/prj/bangaio/master/code/png.py#67 + + +""" +Pure Python PNG Reader/Writer + +This Python module implements support for PNG images (see PNG +specification at http://www.w3.org/TR/2003/REC-PNG-20031110/ ). It reads +and writes PNG files with all allowable bit depths (1/2/4/8/16/24/32/48/64 +bits per pixel) and colour combinations: greyscale (1/2/4/8/16 bit); RGB, +RGBA, LA (greyscale with alpha) with 8/16 bits per channel; colour mapped +images (1/2/4/8 bit). Adam7 interlacing is supported for reading and +writing. A number of optional chunks can be specified (when writing) +and understood (when reading): ``tRNS``, ``bKGD``, ``gAMA``. + +For help, type ``import png; help(png)`` in your python interpreter. + +A good place to start is the :class:`Reader` and :class:`Writer` classes. + +This file can also be used as a command-line utility to convert +`Netpbm `_ PNM files to PNG, and the reverse conversion from PNG to +PNM. The interface is similar to that of the ``pnmtopng`` program from +Netpbm. Type ``python png.py --help`` at the shell prompt +for usage and a list of options. + +A note on spelling and terminology +---------------------------------- + +Generally British English spelling is used in the documentation. So +that's "greyscale" and "colour". This not only matches the author's +native language, it's also used by the PNG specification. + +The major colour models supported by PNG (and hence by PyPNG) are: +greyscale, RGB, greyscale--alpha, RGB--alpha. These are sometimes +referred to using the abbreviations: L, RGB, LA, RGBA. In this case +each letter abbreviates a single channel: *L* is for Luminance or Luma or +Lightness which is the channel used in greyscale images; *R*, *G*, *B* stand +for Red, Green, Blue, the components of a colour image; *A* stands for +Alpha, the opacity channel (used for transparency effects, but higher +values are more opaque, so it makes sense to call it opacity). + +A note on formats +----------------- + +When getting pixel data out of this module (reading) and presenting +data to this module (writing) there are a number of ways the data could +be represented as a Python value. Generally this module uses one of +three formats called "flat row flat pixel", "boxed row flat pixel", and +"boxed row boxed pixel". Basically the concern is whether each pixel +and each row comes in its own little tuple (box), or not. + +Consider an image that is 3 pixels wide by 2 pixels high, and each pixel +has RGB components: + +Boxed row flat pixel:: + + list([R,G,B, R,G,B, R,G,B], + [R,G,B, R,G,B, R,G,B]) + +Each row appears as its own list, but the pixels are flattened so that +three values for one pixel simply follow the three values for the previous +pixel. This is the most common format used, because it provides a good +compromise between space and convenience. PyPNG regards itself as +at liberty to replace any sequence type with any sufficiently compatible +other sequence type; in practice each row is an array (from the array +module), and the outer list is sometimes an iterator rather than an +explicit list (so that streaming is possible). + +Flat row flat pixel:: + + [R,G,B, R,G,B, R,G,B, + R,G,B, R,G,B, R,G,B] + +The entire image is one single giant sequence of colour values. +Generally an array will be used (to save space), not a list. + +Boxed row boxed pixel:: + + list([ (R,G,B), (R,G,B), (R,G,B) ], + [ (R,G,B), (R,G,B), (R,G,B) ]) + +Each row appears in its own list, but each pixel also appears in its own +tuple. A serious memory burn in Python. + +In all cases the top row comes first, and for each row the pixels are +ordered from left-to-right. Within a pixel the values appear in the +order, R-G-B-A (or L-A for greyscale--alpha). + +There is a fourth format, mentioned because it is used internally, +is close to what lies inside a PNG file itself, and has some support +from the public API. This format is called packed. When packed, +each row is a sequence of bytes (integers from 0 to 255), just as +it is before PNG scanline filtering is applied. When the bit depth +is 8 this is essentially the same as boxed row flat pixel; when the +bit depth is less than 8, several pixels are packed into each byte; +when the bit depth is 16 (the only value more than 8 that is supported +by the PNG image format) each pixel value is decomposed into 2 bytes +(and `packed` is a misnomer). This format is used by the +:meth:`Writer.write_packed` method. It isn't usually a convenient +format, but may be just right if the source data for the PNG image +comes from something that uses a similar format (for example, 1-bit +BMPs, or another PNG file). + +And now, my famous members +-------------------------- +""" + +__version__ = "$URL: http://pypng.googlecode.com/svn/trunk/code/png.py $ $Rev: 228 $" + +from pygame.compat import geterror, imap_ +from array import array +from pygame.tests.test_utils import tostring +import itertools +import math +import operator +import struct +import sys +import zlib +import warnings + +__all__ = ["Image", "Reader", "Writer", "write_chunks", "from_array"] + + +# The PNG signature. +# http://www.w3.org/TR/PNG/#5PNG-file-signature +_signature = struct.pack("8B", 137, 80, 78, 71, 13, 10, 26, 10) + +_adam7 = ( + (0, 0, 8, 8), + (4, 0, 8, 8), + (0, 4, 4, 8), + (2, 0, 4, 4), + (0, 2, 2, 4), + (1, 0, 2, 2), + (0, 1, 1, 2), +) + + +def group(s, n): + # See + # http://www.python.org/doc/2.6/library/functions.html#zip + return zip(*[iter(s)] * n) + + +def isarray(x): + """Same as ``isinstance(x, array)``. + """ + return isinstance(x, array) + + +# Conditionally convert to bytes. Works on Python 2 and Python 3. +try: + bytes("", "ascii") + + def strtobytes(x): + return bytes(x, "iso8859-1") + + def bytestostr(x): + return str(x, "iso8859-1") + + +except: + strtobytes = str + bytestostr = str + + +def interleave_planes(ipixels, apixels, ipsize, apsize): + """ + Interleave (colour) planes, e.g. RGB + A = RGBA. + + Return an array of pixels consisting of the `ipsize` elements of data + from each pixel in `ipixels` followed by the `apsize` elements of data + from each pixel in `apixels`. Conventionally `ipixels` and + `apixels` are byte arrays so the sizes are bytes, but it actually + works with any arrays of the same type. The returned array is the + same type as the input arrays which should be the same type as each other. + """ + + itotal = len(ipixels) + atotal = len(apixels) + newtotal = itotal + atotal + newpsize = ipsize + apsize + # Set up the output buffer + # See http://www.python.org/doc/2.4.4/lib/module-array.html#l2h-1356 + out = array(ipixels.typecode) + # It's annoying that there is no cheap way to set the array size :-( + out.extend(ipixels) + out.extend(apixels) + # Interleave in the pixel data + for i in range(ipsize): + out[i:newtotal:newpsize] = ipixels[i:itotal:ipsize] + for i in range(apsize): + out[i + ipsize : newtotal : newpsize] = apixels[i:atotal:apsize] + return out + + +def check_palette(palette): + """Check a palette argument (to the :class:`Writer` class) for validity. + Returns the palette as a list if okay; raises an exception otherwise. + """ + + # None is the default and is allowed. + if palette is None: + return None + + p = list(palette) + if not (0 < len(p) <= 256): + raise ValueError("a palette must have between 1 and 256 entries") + seen_triple = False + for i, t in enumerate(p): + if len(t) not in (3, 4): + raise ValueError("palette entry %d: entries must be 3- or 4-tuples." % i) + if len(t) == 3: + seen_triple = True + if seen_triple and len(t) == 4: + raise ValueError( + "palette entry %d: all 4-tuples must precede all 3-tuples" % i + ) + for x in t: + if int(x) != x or not (0 <= x <= 255): + raise ValueError( + "palette entry %d: values must be integer: 0 <= x <= 255" % i + ) + return p + + +class Error(Exception): + prefix = "Error" + + def __str__(self): + return self.prefix + ": " + " ".join(self.args) + + +class FormatError(Error): + """Problem with input file format. In other words, PNG file does + not conform to the specification in some way and is invalid. + """ + + prefix = "FormatError" + + +class ChunkError(FormatError): + prefix = "ChunkError" + + +class Writer: + """ + PNG encoder in pure Python. + """ + + def __init__( + self, + width=None, + height=None, + size=None, + greyscale=False, + alpha=False, + bitdepth=8, + palette=None, + transparent=None, + background=None, + gamma=None, + compression=None, + interlace=False, + bytes_per_sample=None, # deprecated + planes=None, + colormap=None, + maxval=None, + chunk_limit=2 ** 20, + ): + """ + Create a PNG encoder object. + + Arguments: + + width, height + Image size in pixels, as two separate arguments. + size + Image size (w,h) in pixels, as single argument. + greyscale + Input data is greyscale, not RGB. + alpha + Input data has alpha channel (RGBA or LA). + bitdepth + Bit depth: from 1 to 16. + palette + Create a palette for a colour mapped image (colour type 3). + transparent + Specify a transparent colour (create a ``tRNS`` chunk). + background + Specify a default background colour (create a ``bKGD`` chunk). + gamma + Specify a gamma value (create a ``gAMA`` chunk). + compression + zlib compression level (1-9). + interlace + Create an interlaced image. + chunk_limit + Write multiple ``IDAT`` chunks to save memory. + + The image size (in pixels) can be specified either by using the + `width` and `height` arguments, or with the single `size` + argument. If `size` is used it should be a pair (*width*, + *height*). + + `greyscale` and `alpha` are booleans that specify whether + an image is greyscale (or colour), and whether it has an + alpha channel (or not). + + `bitdepth` specifies the bit depth of the source pixel values. + Each source pixel value must be an integer between 0 and + ``2**bitdepth-1``. For example, 8-bit images have values + between 0 and 255. PNG only stores images with bit depths of + 1,2,4,8, or 16. When `bitdepth` is not one of these values, + the next highest valid bit depth is selected, and an ``sBIT`` + (significant bits) chunk is generated that specifies the original + precision of the source image. In this case the supplied pixel + values will be rescaled to fit the range of the selected bit depth. + + The details of which bit depth / colour model combinations the + PNG file format supports directly, are somewhat arcane + (refer to the PNG specification for full details). Briefly: + "small" bit depths (1,2,4) are only allowed with greyscale and + colour mapped images; colour mapped images cannot have bit depth + 16. + + For colour mapped images (in other words, when the `palette` + argument is specified) the `bitdepth` argument must match one of + the valid PNG bit depths: 1, 2, 4, or 8. (It is valid to have a + PNG image with a palette and an ``sBIT`` chunk, but the meaning + is slightly different; it would be awkward to press the + `bitdepth` argument into service for this.) + + The `palette` option, when specified, causes a colour mapped image + to be created: the PNG colour type is set to 3; greyscale + must not be set; alpha must not be set; transparent must + not be set; the bit depth must be 1,2,4, or 8. When a colour + mapped image is created, the pixel values are palette indexes + and the `bitdepth` argument specifies the size of these indexes + (not the size of the colour values in the palette). + + The palette argument value should be a sequence of 3- or + 4-tuples. 3-tuples specify RGB palette entries; 4-tuples + specify RGBA palette entries. If both 4-tuples and 3-tuples + appear in the sequence then all the 4-tuples must come + before all the 3-tuples. A ``PLTE`` chunk is created; if there + are 4-tuples then a ``tRNS`` chunk is created as well. The + ``PLTE`` chunk will contain all the RGB triples in the same + sequence; the ``tRNS`` chunk will contain the alpha channel for + all the 4-tuples, in the same sequence. Palette entries + are always 8-bit. + + If specified, the `transparent` and `background` parameters must + be a tuple with three integer values for red, green, blue, or + a simple integer (or singleton tuple) for a greyscale image. + + If specified, the `gamma` parameter must be a positive number + (generally, a float). A ``gAMA`` chunk will be created. Note that + this will not change the values of the pixels as they appear in + the PNG file, they are assumed to have already been converted + appropriately for the gamma specified. + + The `compression` argument specifies the compression level + to be used by the ``zlib`` module. Higher values are likely + to compress better, but will be slower to compress. The + default for this argument is ``None``; this does not mean + no compression, rather it means that the default from the + ``zlib`` module is used (which is generally acceptable). + + If `interlace` is true then an interlaced image is created + (using PNG's so far only interlace method, *Adam7*). This does not + affect how the pixels should be presented to the encoder, rather + it changes how they are arranged into the PNG file. On slow + connexions interlaced images can be partially decoded by the + browser to give a rough view of the image that is successively + refined as more image data appears. + + .. note :: + + Enabling the `interlace` option requires the entire image + to be processed in working memory. + + `chunk_limit` is used to limit the amount of memory used whilst + compressing the image. In order to avoid using large amounts of + memory, multiple ``IDAT`` chunks may be created. + """ + + # At the moment the `planes` argument is ignored; + # its purpose is to act as a dummy so that + # ``Writer(x, y, **info)`` works, where `info` is a dictionary + # returned by Reader.read and friends. + # Ditto for `colormap`. + + # A couple of helper functions come first. Best skipped if you + # are reading through. + + def isinteger(x): + try: + return int(x) == x + except: + return False + + def check_color(c, which): + """Checks that a colour argument for transparent or + background options is the right form. Also "corrects" bare + integers to 1-tuples. + """ + + if c is None: + return c + if greyscale: + try: + l = len(c) + except TypeError: + c = (c,) + if len(c) != 1: + raise ValueError("%s for greyscale must be 1-tuple" % which) + if not isinteger(c[0]): + raise ValueError("%s colour for greyscale must be integer" % which) + else: + if not ( + len(c) == 3 + and isinteger(c[0]) + and isinteger(c[1]) + and isinteger(c[2]) + ): + raise ValueError("%s colour must be a triple of integers" % which) + return c + + if size: + if len(size) != 2: + raise ValueError("size argument should be a pair (width, height)") + if width is not None and width != size[0]: + raise ValueError( + "size[0] (%r) and width (%r) should match when both are used." + % (size[0], width) + ) + if height is not None and height != size[1]: + raise ValueError( + "size[1] (%r) and height (%r) should match when both are used." + % (size[1], height) + ) + width, height = size + del size + + if width <= 0 or height <= 0: + raise ValueError("width and height must be greater than zero") + if not isinteger(width) or not isinteger(height): + raise ValueError("width and height must be integers") + # http://www.w3.org/TR/PNG/#7Integers-and-byte-order + if width > 2 ** 32 - 1 or height > 2 ** 32 - 1: + raise ValueError("width and height cannot exceed 2**32-1") + + if alpha and transparent is not None: + raise ValueError("transparent colour not allowed with alpha channel") + + if bytes_per_sample is not None: + warnings.warn( + "please use bitdepth instead of bytes_per_sample", DeprecationWarning + ) + if bytes_per_sample not in (0.125, 0.25, 0.5, 1, 2): + raise ValueError("bytes per sample must be .125, .25, .5, 1, or 2") + bitdepth = int(8 * bytes_per_sample) + del bytes_per_sample + if not isinteger(bitdepth) or bitdepth < 1 or 16 < bitdepth: + raise ValueError("bitdepth (%r) must be a positive integer <= 16" + % bitdepth) + + self.rescale = None + if palette: + if bitdepth not in (1, 2, 4, 8): + raise ValueError("with palette, bitdepth must be 1, 2, 4, or 8") + if transparent is not None: + raise ValueError("transparent and palette not compatible") + if alpha: + raise ValueError("alpha and palette not compatible") + if greyscale: + raise ValueError("greyscale and palette not compatible") + else: + # No palette, check for sBIT chunk generation. + if alpha or not greyscale: + if bitdepth not in (8, 16): + targetbitdepth = (8, 16)[bitdepth > 8] + self.rescale = (bitdepth, targetbitdepth) + bitdepth = targetbitdepth + del targetbitdepth + else: + assert greyscale + assert not alpha + if bitdepth not in (1, 2, 4, 8, 16): + if bitdepth > 8: + targetbitdepth = 16 + elif bitdepth == 3: + targetbitdepth = 4 + else: + assert bitdepth in (5, 6, 7) + targetbitdepth = 8 + self.rescale = (bitdepth, targetbitdepth) + bitdepth = targetbitdepth + del targetbitdepth + + if bitdepth < 8 and (alpha or not greyscale and not palette): + raise ValueError("bitdepth < 8 only permitted with greyscale or palette") + if bitdepth > 8 and palette: + raise ValueError("bit depth must be 8 or less for images with palette") + + transparent = check_color(transparent, "transparent") + background = check_color(background, "background") + + # It's important that the true boolean values (greyscale, alpha, + # colormap, interlace) are converted to bool because Iverson's + # convention is relied upon later on. + self.width = width + self.height = height + self.transparent = transparent + self.background = background + self.gamma = gamma + self.greyscale = bool(greyscale) + self.alpha = bool(alpha) + self.colormap = bool(palette) + self.bitdepth = int(bitdepth) + self.compression = compression + self.chunk_limit = chunk_limit + self.interlace = bool(interlace) + self.palette = check_palette(palette) + + self.color_type = 4 * self.alpha + 2 * (not greyscale) + 1 * self.colormap + assert self.color_type in (0, 2, 3, 4, 6) + + self.color_planes = (3, 1)[self.greyscale or self.colormap] + self.planes = self.color_planes + self.alpha + # :todo: fix for bitdepth < 8 + self.psize = (self.bitdepth / 8) * self.planes + + def make_palette(self): + """Create the byte sequences for a ``PLTE`` and if necessary a + ``tRNS`` chunk. Returned as a pair (*p*, *t*). *t* will be + ``None`` if no ``tRNS`` chunk is necessary. + """ + + p = array("B") + t = array("B") + + for x in self.palette: + p.extend(x[0:3]) + if len(x) > 3: + t.append(x[3]) + p = tostring(p) + t = tostring(t) + if t: + return p, t + return p, None + + def write(self, outfile, rows): + """Write a PNG image to the output file. `rows` should be + an iterable that yields each row in boxed row flat pixel format. + The rows should be the rows of the original image, so there + should be ``self.height`` rows of ``self.width * self.planes`` values. + If `interlace` is specified (when creating the instance), then + an interlaced PNG file will be written. Supply the rows in the + normal image order; the interlacing is carried out internally. + + .. note :: + + Interlacing will require the entire image to be in working memory. + """ + + if self.interlace: + fmt = "BH"[self.bitdepth > 8] + a = array(fmt, itertools.chain(*rows)) + return self.write_array(outfile, a) + else: + nrows = self.write_passes(outfile, rows) + if nrows != self.height: + raise ValueError( + "rows supplied (%d) does not match height (%d)" + % (nrows, self.height) + ) + + def write_passes(self, outfile, rows, packed=False): + """ + Write a PNG image to the output file. + + Most users are expected to find the :meth:`write` or + :meth:`write_array` method more convenient. + + The rows should be given to this method in the order that + they appear in the output file. For straightlaced images, + this is the usual top to bottom ordering, but for interlaced + images the rows should have already been interlaced before + passing them to this function. + + `rows` should be an iterable that yields each row. When + `packed` is ``False`` the rows should be in boxed row flat pixel + format; when `packed` is ``True`` each row should be a packed + sequence of bytes. + + """ + + # http://www.w3.org/TR/PNG/#5PNG-file-signature + outfile.write(_signature) + + # http://www.w3.org/TR/PNG/#11IHDR + write_chunk( + outfile, + "IHDR", + struct.pack( + "!2I5B", + self.width, + self.height, + self.bitdepth, + self.color_type, + 0, + 0, + self.interlace, + ), + ) + + # See :chunk:order + # http://www.w3.org/TR/PNG/#11gAMA + if self.gamma is not None: + write_chunk( + outfile, "gAMA", struct.pack("!L", int(round(self.gamma * 1e5))) + ) + + # See :chunk:order + # http://www.w3.org/TR/PNG/#11sBIT + if self.rescale: + write_chunk( + outfile, + "sBIT", + struct.pack("%dB" % self.planes, *[self.rescale[0]] * self.planes), + ) + + # :chunk:order: Without a palette (PLTE chunk), ordering is + # relatively relaxed. With one, gAMA chunk must precede PLTE + # chunk which must precede tRNS and bKGD. + # See http://www.w3.org/TR/PNG/#5ChunkOrdering + if self.palette: + p, t = self.make_palette() + write_chunk(outfile, "PLTE", p) + if t: + # tRNS chunk is optional. Only needed if palette entries + # have alpha. + write_chunk(outfile, "tRNS", t) + + # http://www.w3.org/TR/PNG/#11tRNS + if self.transparent is not None: + if self.greyscale: + write_chunk(outfile, "tRNS", struct.pack("!1H", *self.transparent)) + else: + write_chunk(outfile, "tRNS", struct.pack("!3H", *self.transparent)) + + # http://www.w3.org/TR/PNG/#11bKGD + if self.background is not None: + if self.greyscale: + write_chunk(outfile, "bKGD", struct.pack("!1H", *self.background)) + else: + write_chunk(outfile, "bKGD", struct.pack("!3H", *self.background)) + + # http://www.w3.org/TR/PNG/#11IDAT + if self.compression is not None: + compressor = zlib.compressobj(self.compression) + else: + compressor = zlib.compressobj() + + # Choose an extend function based on the bitdepth. The extend + # function packs/decomposes the pixel values into bytes and + # stuffs them onto the data array. + data = array("B") + if self.bitdepth == 8 or packed: + extend = data.extend + elif self.bitdepth == 16: + # Decompose into bytes + def extend(sl): + fmt = "!%dH" % len(sl) + data.extend(array("B", struct.pack(fmt, *sl))) + + else: + # Pack into bytes + assert self.bitdepth < 8 + # samples per byte + spb = int(8 / self.bitdepth) + + def extend(sl): + a = array("B", sl) + # Adding padding bytes so we can group into a whole + # number of spb-tuples. + l = float(len(a)) + extra = math.ceil(l / float(spb)) * spb - l + a.extend([0] * int(extra)) + # Pack into bytes + l = group(a, spb) + l = map(lambda e: reduce(lambda x, y: (x << self.bitdepth) + y, e), l) + data.extend(l) + + if self.rescale: + oldextend = extend + factor = float(2 ** self.rescale[1] - 1) / float(2 ** self.rescale[0] - 1) + + def extend(sl): + oldextend(map(lambda x: int(round(factor * x)), sl)) + + # Build the first row, testing mostly to see if we need to + # changed the extend function to cope with NumPy integer types + # (they cause our ordinary definition of extend to fail, so we + # wrap it). See + # http://code.google.com/p/pypng/issues/detail?id=44 + enumrows = enumerate(rows) + del rows + + # First row's filter type. + data.append(0) + # :todo: Certain exceptions in the call to ``.next()`` or the + # following try would indicate no row data supplied. + # Should catch. + i, row = next(enumrows) + try: + # If this fails... + extend(row) + except: + # ... try a version that converts the values to int first. + # Not only does this work for the (slightly broken) NumPy + # types, there are probably lots of other, unknown, "nearly" + # int types it works for. + def wrapmapint(f): + return lambda sl: f(map(int, sl)) + + extend = wrapmapint(extend) + del wrapmapint + extend(row) + + for i, row in enumrows: + # Add "None" filter type. Currently, it's essential that + # this filter type be used for every scanline as we do not + # mark the first row of a reduced pass image; that means we + # could accidentally compute the wrong filtered scanline if + # we used "up", "average", or "paeth" on such a line. + data.append(0) + extend(row) + if len(data) > self.chunk_limit: + compressed = compressor.compress(tostring(data)) + if len(compressed): + # print >> sys.stderr, len(data), len(compressed) + write_chunk(outfile, "IDAT", compressed) + # Because of our very witty definition of ``extend``, + # above, we must re-use the same ``data`` object. Hence + # we use ``del`` to empty this one, rather than create a + # fresh one (which would be my natural FP instinct). + del data[:] + if len(data): + compressed = compressor.compress(tostring(data)) + else: + compressed = "" + flushed = compressor.flush() + if len(compressed) or len(flushed): + # print >> sys.stderr, len(data), len(compressed), len(flushed) + write_chunk(outfile, "IDAT", compressed + flushed) + # http://www.w3.org/TR/PNG/#11IEND + write_chunk(outfile, "IEND") + return i + 1 + + def write_array(self, outfile, pixels): + """ + Write an array in flat row flat pixel format as a PNG file on + the output file. See also :meth:`write` method. + """ + + if self.interlace: + self.write_passes(outfile, self.array_scanlines_interlace(pixels)) + else: + self.write_passes(outfile, self.array_scanlines(pixels)) + + def write_packed(self, outfile, rows): + """ + Write PNG file to `outfile`. The pixel data comes from `rows` + which should be in boxed row packed format. Each row should be + a sequence of packed bytes. + + Technically, this method does work for interlaced images but it + is best avoided. For interlaced images, the rows should be + presented in the order that they appear in the file. + + This method should not be used when the source image bit depth + is not one naturally supported by PNG; the bit depth should be + 1, 2, 4, 8, or 16. + """ + + if self.rescale: + raise Error( + "write_packed method not suitable for bit depth %d" % self.rescale[0] + ) + return self.write_passes(outfile, rows, packed=True) + + def convert_pnm(self, infile, outfile): + """ + Convert a PNM file containing raw pixel data into a PNG file + with the parameters set in the writer object. Works for + (binary) PGM, PPM, and PAM formats. + """ + + if self.interlace: + pixels = array("B") + pixels.fromfile( + infile, + (self.bitdepth / 8) * self.color_planes * self.width * self.height, + ) + self.write_passes(outfile, self.array_scanlines_interlace(pixels)) + else: + self.write_passes(outfile, self.file_scanlines(infile)) + + def convert_ppm_and_pgm(self, ppmfile, pgmfile, outfile): + """ + Convert a PPM and PGM file containing raw pixel data into a + PNG outfile with the parameters set in the writer object. + """ + pixels = array("B") + pixels.fromfile( + ppmfile, (self.bitdepth / 8) * self.color_planes * self.width * self.height + ) + apixels = array("B") + apixels.fromfile(pgmfile, (self.bitdepth / 8) * self.width * self.height) + pixels = interleave_planes( + pixels, + apixels, + (self.bitdepth / 8) * self.color_planes, + (self.bitdepth / 8), + ) + if self.interlace: + self.write_passes(outfile, self.array_scanlines_interlace(pixels)) + else: + self.write_passes(outfile, self.array_scanlines(pixels)) + + def file_scanlines(self, infile): + """ + Generates boxed rows in flat pixel format, from the input file + `infile`. It assumes that the input file is in a "Netpbm-like" + binary format, and is positioned at the beginning of the first + pixel. The number of pixels to read is taken from the image + dimensions (`width`, `height`, `planes`) and the number of bytes + per value is implied by the image `bitdepth`. + """ + + # Values per row + vpr = self.width * self.planes + row_bytes = vpr + if self.bitdepth > 8: + assert self.bitdepth == 16 + row_bytes *= 2 + fmt = ">%dH" % vpr + + def line(): + return array("H", struct.unpack(fmt, infile.read(row_bytes))) + + else: + + def line(): + scanline = array("B", infile.read(row_bytes)) + return scanline + + for y in range(self.height): + yield line() + + def array_scanlines(self, pixels): + """ + Generates boxed rows (flat pixels) from flat rows (flat pixels) + in an array. + """ + + # Values per row + vpr = self.width * self.planes + stop = 0 + for y in range(self.height): + start = stop + stop = start + vpr + yield pixels[start:stop] + + def array_scanlines_interlace(self, pixels): + """ + Generator for interlaced scanlines from an array. `pixels` is + the full source image in flat row flat pixel format. The + generator yields each scanline of the reduced passes in turn, in + boxed row flat pixel format. + """ + + # http://www.w3.org/TR/PNG/#8InterlaceMethods + # Array type. + fmt = "BH"[self.bitdepth > 8] + # Value per row + vpr = self.width * self.planes + for xstart, ystart, xstep, ystep in _adam7: + if xstart >= self.width: + continue + # Pixels per row (of reduced image) + ppr = int(math.ceil((self.width - xstart) / float(xstep))) + # number of values in reduced image row. + row_len = ppr * self.planes + for y in range(ystart, self.height, ystep): + if xstep == 1: + offset = y * vpr + yield pixels[offset : offset + vpr] + else: + row = array(fmt) + # There's no easier way to set the length of an array + row.extend(pixels[0:row_len]) + offset = y * vpr + xstart * self.planes + end_offset = (y + 1) * vpr + skip = self.planes * xstep + for i in range(self.planes): + row[i :: self.planes] = pixels[offset + i : end_offset : skip] + yield row + + +def write_chunk(outfile, tag, data=strtobytes("")): + """ + Write a PNG chunk to the output file, including length and + checksum. + """ + + # http://www.w3.org/TR/PNG/#5Chunk-layout + outfile.write(struct.pack("!I", len(data))) + tag = strtobytes(tag) + outfile.write(tag) + outfile.write(data) + checksum = zlib.crc32(tag) + checksum = zlib.crc32(data, checksum) + checksum &= 2 ** 32 - 1 + outfile.write(struct.pack("!I", checksum)) + + +def write_chunks(out, chunks): + """Create a PNG file by writing out the chunks.""" + + out.write(_signature) + for chunk in chunks: + write_chunk(out, *chunk) + + +def filter_scanline(type, line, fo, prev=None): + """Apply a scanline filter to a scanline. `type` specifies the + filter type (0 to 4); `line` specifies the current (unfiltered) + scanline as a sequence of bytes; `prev` specifies the previous + (unfiltered) scanline as a sequence of bytes. `fo` specifies the + filter offset; normally this is size of a pixel in bytes (the number + of bytes per sample times the number of channels), but when this is + < 1 (for bit depths < 8) then the filter offset is 1. + """ + + assert 0 <= type < 5 + + # The output array. Which, pathetically, we extend one-byte at a + # time (fortunately this is linear). + out = array("B", [type]) + + def sub(): + ai = -fo + for x in line: + if ai >= 0: + x = (x - line[ai]) & 0xFF + out.append(x) + ai += 1 + + def up(): + for i, x in enumerate(line): + x = (x - prev[i]) & 0xFF + out.append(x) + + def average(): + ai = -fo + for i, x in enumerate(line): + if ai >= 0: + x = (x - ((line[ai] + prev[i]) >> 1)) & 0xFF + else: + x = (x - (prev[i] >> 1)) & 0xFF + out.append(x) + ai += 1 + + def paeth(): + # http://www.w3.org/TR/PNG/#9Filter-type-4-Paeth + ai = -fo # also used for ci + for i, x in enumerate(line): + a = 0 + b = prev[i] + c = 0 + + if ai >= 0: + a = line[ai] + c = prev[ai] + p = a + b - c + pa = abs(p - a) + pb = abs(p - b) + pc = abs(p - c) + if pa <= pb and pa <= pc: + Pr = a + elif pb <= pc: + Pr = b + else: + Pr = c + + x = (x - Pr) & 0xFF + out.append(x) + ai += 1 + + if not prev: + # We're on the first line. Some of the filters can be reduced + # to simpler cases which makes handling the line "off the top" + # of the image simpler. "up" becomes "none"; "paeth" becomes + # "left" (non-trivial, but true). "average" needs to be handled + # specially. + if type == 2: # "up" + return line # type = 0 + elif type == 3: + prev = [0] * len(line) + elif type == 4: # "paeth" + type = 1 + if type == 0: + out.extend(line) + elif type == 1: + sub() + elif type == 2: + up() + elif type == 3: + average() + else: # type == 4 + paeth() + return out + + +def from_array(a, mode=None, info={}): + """Create a PNG :class:`Image` object from a 2- or 3-dimensional array. + One application of this function is easy PIL-style saving: + ``png.from_array(pixels, 'L').save('foo.png')``. + + .. note : + + The use of the term *3-dimensional* is for marketing purposes + only. It doesn't actually work. Please bear with us. Meanwhile + enjoy the complimentary snacks (on request) and please use a + 2-dimensional array. + + Unless they are specified using the *info* parameter, the PNG's + height and width are taken from the array size. For a 3 dimensional + array the first axis is the height; the second axis is the width; + and the third axis is the channel number. Thus an RGB image that is + 16 pixels high and 8 wide will use an array that is 16x8x3. For 2 + dimensional arrays the first axis is the height, but the second axis + is ``width*channels``, so an RGB image that is 16 pixels high and 8 + wide will use a 2-dimensional array that is 16x24 (each row will be + 8*3==24 sample values). + + *mode* is a string that specifies the image colour format in a + PIL-style mode. It can be: + + ``'L'`` + greyscale (1 channel) + ``'LA'`` + greyscale with alpha (2 channel) + ``'RGB'`` + colour image (3 channel) + ``'RGBA'`` + colour image with alpha (4 channel) + + The mode string can also specify the bit depth (overriding how this + function normally derives the bit depth, see below). Appending + ``';16'`` to the mode will cause the PNG to be 16 bits per channel; + any decimal from 1 to 16 can be used to specify the bit depth. + + When a 2-dimensional array is used *mode* determines how many + channels the image has, and so allows the width to be derived from + the second array dimension. + + The array is expected to be a ``numpy`` array, but it can be any + suitable Python sequence. For example, a list of lists can be used: + ``png.from_array([[0, 255, 0], [255, 0, 255]], 'L')``. The exact + rules are: ``len(a)`` gives the first dimension, height; + ``len(a[0])`` gives the second dimension; ``len(a[0][0])`` gives the + third dimension, unless an exception is raised in which case a + 2-dimensional array is assumed. It's slightly more complicated than + that because an iterator of rows can be used, and it all still + works. Using an iterator allows data to be streamed efficiently. + + The bit depth of the PNG is normally taken from the array element's + datatype (but if *mode* specifies a bitdepth then that is used + instead). The array element's datatype is determined in a way which + is supposed to work both for ``numpy`` arrays and for Python + ``array.array`` objects. A 1 byte datatype will give a bit depth of + 8, a 2 byte datatype will give a bit depth of 16. If the datatype + does not have an implicit size, for example it is a plain Python + list of lists, as above, then a default of 8 is used. + + The *info* parameter is a dictionary that can be used to specify + metadata (in the same style as the arguments to the + :class:``png.Writer`` class). For this function the keys that are + useful are: + + height + overrides the height derived from the array dimensions and allows + *a* to be an iterable. + width + overrides the width derived from the array dimensions. + bitdepth + overrides the bit depth derived from the element datatype (but + must match *mode* if that also specifies a bit depth). + + Generally anything specified in the + *info* dictionary will override any implicit choices that this + function would otherwise make, but must match any explicit ones. + For example, if the *info* dictionary has a ``greyscale`` key then + this must be true when mode is ``'L'`` or ``'LA'`` and false when + mode is ``'RGB'`` or ``'RGBA'``. + """ + + # We abuse the *info* parameter by modifying it. Take a copy here. + # (Also typechecks *info* to some extent). + info = dict(info) + + # Syntax check mode string. + bitdepth = None + try: + mode = mode.split(";") + if len(mode) not in (1, 2): + raise Error() + if mode[0] not in ("L", "LA", "RGB", "RGBA"): + raise Error() + if len(mode) == 2: + try: + bitdepth = int(mode[1]) + except: + raise Error() + except Error: + raise Error("mode string should be 'RGB' or 'L;16' or similar.") + mode = mode[0] + + # Get bitdepth from *mode* if possible. + if bitdepth: + if info.get("bitdepth") and bitdepth != info["bitdepth"]: + raise Error( + "mode bitdepth (%d) should match info bitdepth (%d)." + % (bitdepth, info["bitdepth"]) + ) + info["bitdepth"] = bitdepth + + # Fill in and/or check entries in *info*. + # Dimensions. + if "size" in info: + # Check width, height, size all match where used. + for dimension, axis in [("width", 0), ("height", 1)]: + if dimension in info: + if info[dimension] != info["size"][axis]: + raise Error( + "info[%r] should match info['size'][%r]." % (dimension, axis) + ) + info["width"], info["height"] = info["size"] + if "height" not in info: + try: + l = len(a) + except: + raise Error("len(a) does not work, supply info['height'] instead.") + info["height"] = l + # Colour format. + if "greyscale" in info: + if bool(info["greyscale"]) != ("L" in mode): + raise Error("info['greyscale'] should match mode.") + info["greyscale"] = "L" in mode + if "alpha" in info: + if bool(info["alpha"]) != ("A" in mode): + raise Error("info['alpha'] should match mode.") + info["alpha"] = "A" in mode + + planes = len(mode) + if "planes" in info: + if info["planes"] != planes: + raise Error("info['planes'] should match mode.") + + # In order to work out whether we the array is 2D or 3D we need its + # first row, which requires that we take a copy of its iterator. + # We may also need the first row to derive width and bitdepth. + a, t = itertools.tee(a) + row = next(t) + del t + try: + row[0][0] + threed = True + testelement = row[0] + except: + threed = False + testelement = row + if "width" not in info: + if threed: + width = len(row) + else: + width = len(row) // planes + info["width"] = width + + # Not implemented yet + assert not threed + + if "bitdepth" not in info: + try: + dtype = testelement.dtype + # goto the "else:" clause. Sorry. + except: + try: + # Try a Python array.array. + bitdepth = 8 * testelement.itemsize + except: + # We can't determine it from the array element's + # datatype, use a default of 8. + bitdepth = 8 + else: + # If we got here without exception, we now assume that + # the array is a numpy array. + if dtype.kind == "b": + bitdepth = 1 + else: + bitdepth = 8 * dtype.itemsize + info["bitdepth"] = bitdepth + + for thing in "width height bitdepth greyscale alpha".split(): + assert thing in info + return Image(a, info) + + +# So that refugee's from PIL feel more at home. Not documented. +fromarray = from_array + + +class Image: + """A PNG image. + You can create an :class:`Image` object from an array of pixels by calling + :meth:`png.from_array`. It can be saved to disk with the + :meth:`save` method.""" + + def __init__(self, rows, info): + """ + .. note :: + + The constructor is not public. Please do not call it. + """ + + self.rows = rows + self.info = info + + def save(self, file): + """Save the image to *file*. If *file* looks like an open file + descriptor then it is used, otherwise it is treated as a + filename and a fresh file is opened. + + In general, you can only call this method once; after it has + been called the first time and the PNG image has been saved, the + source data will have been streamed, and cannot be streamed + again. + """ + + w = Writer(**self.info) + + try: + file.write + + def close(): + pass + + except: + file = open(file, "wb") + + def close(): + file.close() + + try: + w.write(file, self.rows) + finally: + close() + + +class _readable: + """ + A simple file-like interface for strings and arrays. + """ + + def __init__(self, buf): + self.buf = buf + self.offset = 0 + + def read(self, n): + r = self.buf[self.offset : self.offset + n] + if isarray(r): + r = tostring(r) + self.offset += n + return r + + +class Reader: + """ + PNG decoder in pure Python. + """ + + def __init__(self, _guess=None, **kw): + """ + Create a PNG decoder object. + + The constructor expects exactly one keyword argument. If you + supply a positional argument instead, it will guess the input + type. You can choose among the following keyword arguments: + + filename + Name of input file (a PNG file). + file + A file-like object (object with a read() method). + bytes + ``array`` or ``string`` with PNG data. + + """ + if (_guess is not None and len(kw) != 0) or (_guess is None and len(kw) != 1): + raise TypeError("Reader() takes exactly 1 argument") + + # Will be the first 8 bytes, later on. See validate_signature. + self.signature = None + self.transparent = None + # A pair of (len,type) if a chunk has been read but its data and + # checksum have not (in other words the file position is just + # past the 4 bytes that specify the chunk type). See preamble + # method for how this is used. + self.atchunk = None + + if _guess is not None: + if isarray(_guess): + kw["bytes"] = _guess + elif isinstance(_guess, str): + kw["filename"] = _guess + elif isinstance(_guess, file): + kw["file"] = _guess + + if "filename" in kw: + self.file = open(kw["filename"], "rb") + elif "file" in kw: + self.file = kw["file"] + elif "bytes" in kw: + self.file = _readable(kw["bytes"]) + else: + raise TypeError("expecting filename, file or bytes array") + + def chunk(self, seek=None): + """ + Read the next PNG chunk from the input file; returns a + (*type*,*data*) tuple. *type* is the chunk's type as a string + (all PNG chunk types are 4 characters long). *data* is the + chunk's data content, as a string. + + If the optional `seek` argument is + specified then it will keep reading chunks until it either runs + out of file or finds the type specified by the argument. Note + that in general the order of chunks in PNGs is unspecified, so + using `seek` can cause you to miss chunks. + """ + + self.validate_signature() + + while True: + # http://www.w3.org/TR/PNG/#5Chunk-layout + if not self.atchunk: + self.atchunk = self.chunklentype() + length, type = self.atchunk + self.atchunk = None + data = self.file.read(length) + if len(data) != length: + raise ChunkError( + "Chunk %s too short for required %i octets." % (type, length) + ) + checksum = self.file.read(4) + if len(checksum) != 4: + raise ValueError("Chunk %s too short for checksum.", tag) + if seek and type != seek: + continue + verify = zlib.crc32(strtobytes(type)) + verify = zlib.crc32(data, verify) + # Whether the output from zlib.crc32 is signed or not varies + # according to hideous implementation details, see + # http://bugs.python.org/issue1202 . + # We coerce it to be positive here (in a way which works on + # Python 2.3 and older). + verify &= 2 ** 32 - 1 + verify = struct.pack("!I", verify) + if checksum != verify: + # print repr(checksum) + (a,) = struct.unpack("!I", checksum) + (b,) = struct.unpack("!I", verify) + raise ChunkError( + "Checksum error in %s chunk: 0x%08X != 0x%08X." % (type, a, b) + ) + return type, data + + def chunks(self): + """Return an iterator that will yield each chunk as a + (*chunktype*, *content*) pair. + """ + + while True: + t, v = self.chunk() + yield t, v + if t == "IEND": + break + + def undo_filter(self, filter_type, scanline, previous): + """Undo the filter for a scanline. `scanline` is a sequence of + bytes that does not include the initial filter type byte. + `previous` is decoded previous scanline (for straightlaced + images this is the previous pixel row, but for interlaced + images, it is the previous scanline in the reduced image, which + in general is not the previous pixel row in the final image). + When there is no previous scanline (the first row of a + straightlaced image, or the first row in one of the passes in an + interlaced image), then this argument should be ``None``. + + The scanline will have the effects of filtering removed, and the + result will be returned as a fresh sequence of bytes. + """ + + # :todo: Would it be better to update scanline in place? + + # Create the result byte array. It seems that the best way to + # create the array to be the right size is to copy from an + # existing sequence. *sigh* + # If we fill the result with scanline, then this allows a + # micro-optimisation in the "null" and "sub" cases. + result = array("B", scanline) + + if filter_type == 0: + # And here, we _rely_ on filling the result with scanline, + # above. + return result + + if filter_type not in (1, 2, 3, 4): + raise FormatError( + "Invalid PNG Filter Type." + " See http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters ." + ) + + # Filter unit. The stride from one pixel to the corresponding + # byte from the previous previous. Normally this is the pixel + # size in bytes, but when this is smaller than 1, the previous + # byte is used instead. + fu = max(1, self.psize) + + # For the first line of a pass, synthesize a dummy previous + # line. An alternative approach would be to observe that on the + # first line 'up' is the same as 'null', 'paeth' is the same + # as 'sub', with only 'average' requiring any special case. + if not previous: + previous = array("B", [0] * len(scanline)) + + def sub(): + """Undo sub filter.""" + + ai = 0 + # Loops starts at index fu. Observe that the initial part + # of the result is already filled in correctly with + # scanline. + for i in range(fu, len(result)): + x = scanline[i] + a = result[ai] + result[i] = (x + a) & 0xFF + ai += 1 + + def up(): + """Undo up filter.""" + + for i in range(len(result)): + x = scanline[i] + b = previous[i] + result[i] = (x + b) & 0xFF + + def average(): + """Undo average filter.""" + + ai = -fu + for i in range(len(result)): + x = scanline[i] + if ai < 0: + a = 0 + else: + a = result[ai] + b = previous[i] + result[i] = (x + ((a + b) >> 1)) & 0xFF + ai += 1 + + def paeth(): + """Undo Paeth filter.""" + + # Also used for ci. + ai = -fu + for i in range(len(result)): + x = scanline[i] + if ai < 0: + a = c = 0 + else: + a = result[ai] + c = previous[ai] + b = previous[i] + p = a + b - c + pa = abs(p - a) + pb = abs(p - b) + pc = abs(p - c) + if pa <= pb and pa <= pc: + pr = a + elif pb <= pc: + pr = b + else: + pr = c + result[i] = (x + pr) & 0xFF + ai += 1 + + # Call appropriate filter algorithm. Note that 0 has already + # been dealt with. + (None, sub, up, average, paeth)[filter_type]() + return result + + def deinterlace(self, raw): + """ + Read raw pixel data, undo filters, deinterlace, and flatten. + Return in flat row flat pixel format. + """ + + # print >> sys.stderr, ("Reading interlaced, w=%s, r=%s, planes=%s," + + # " bpp=%s") % (self.width, self.height, self.planes, self.bps) + # Values per row (of the target image) + vpr = self.width * self.planes + + # Make a result array, and make it big enough. Interleaving + # writes to the output array randomly (well, not quite), so the + # entire output array must be in memory. + fmt = "BH"[self.bitdepth > 8] + a = array(fmt, [0] * vpr * self.height) + source_offset = 0 + + for xstart, ystart, xstep, ystep in _adam7: + # print >> sys.stderr, "Adam7: start=%s,%s step=%s,%s" % ( + # xstart, ystart, xstep, ystep) + if xstart >= self.width: + continue + # The previous (reconstructed) scanline. None at the + # beginning of a pass to indicate that there is no previous + # line. + recon = None + # Pixels per row (reduced pass image) + ppr = int(math.ceil((self.width - xstart) / float(xstep))) + # Row size in bytes for this pass. + row_size = int(math.ceil(self.psize * ppr)) + for y in range(ystart, self.height, ystep): + filter_type = raw[source_offset] + source_offset += 1 + scanline = raw[source_offset : source_offset + row_size] + source_offset += row_size + recon = self.undo_filter(filter_type, scanline, recon) + # Convert so that there is one element per pixel value + flat = self.serialtoflat(recon, ppr) + if xstep == 1: + assert xstart == 0 + offset = y * vpr + a[offset : offset + vpr] = flat + else: + offset = y * vpr + xstart * self.planes + end_offset = (y + 1) * vpr + skip = self.planes * xstep + for i in range(self.planes): + a[offset + i : end_offset : skip] = flat[i :: self.planes] + return a + + def iterboxed(self, rows): + """Iterator that yields each scanline in boxed row flat pixel + format. `rows` should be an iterator that yields the bytes of + each row in turn. + """ + + def asvalues(raw): + """Convert a row of raw bytes into a flat row. Result may + or may not share with argument""" + + if self.bitdepth == 8: + return raw + if self.bitdepth == 16: + raw = tostring(raw) + return array("H", struct.unpack("!%dH" % (len(raw) // 2), raw)) + assert self.bitdepth < 8 + width = self.width + # Samples per byte + spb = 8 // self.bitdepth + out = array("B") + mask = 2 ** self.bitdepth - 1 + shifts = map(self.bitdepth.__mul__, reversed(range(spb))) + for o in raw: + out.extend(map(lambda i: mask & (o >> i), shifts)) + return out[:width] + + return imap_(asvalues, rows) + + def serialtoflat(self, bytes, width=None): + """Convert serial format (byte stream) pixel data to flat row + flat pixel. + """ + + if self.bitdepth == 8: + return bytes + if self.bitdepth == 16: + bytes = tostring(bytes) + return array("H", struct.unpack("!%dH" % (len(bytes) // 2), bytes)) + assert self.bitdepth < 8 + if width is None: + width = self.width + # Samples per byte + spb = 8 // self.bitdepth + out = array("B") + mask = 2 ** self.bitdepth - 1 + shifts = map(self.bitdepth.__mul__, reversed(range(spb))) + l = width + for o in bytes: + out.extend([(mask & (o >> s)) for s in shifts][:l]) + l -= spb + if l <= 0: + l = width + return out + + def iterstraight(self, raw): + """Iterator that undoes the effect of filtering, and yields each + row in serialised format (as a sequence of bytes). Assumes input + is straightlaced. `raw` should be an iterable that yields the + raw bytes in chunks of arbitrary size.""" + + # length of row, in bytes + rb = self.row_bytes + a = array("B") + # The previous (reconstructed) scanline. None indicates first + # line of image. + recon = None + for some in raw: + a.extend(some) + while len(a) >= rb + 1: + filter_type = a[0] + scanline = a[1 : rb + 1] + del a[: rb + 1] + recon = self.undo_filter(filter_type, scanline, recon) + yield recon + if len(a) != 0: + # :file:format We get here with a file format error: when the + # available bytes (after decompressing) do not pack into exact + # rows. + raise FormatError("Wrong size for decompressed IDAT chunk.") + assert len(a) == 0 + + def validate_signature(self): + """If signature (header) has not been read then read and + validate it; otherwise do nothing. + """ + + if self.signature: + return + self.signature = self.file.read(8) + if self.signature != _signature: + raise FormatError("PNG file has invalid signature.") + + def preamble(self): + """ + Extract the image metadata by reading the initial part of the PNG + file up to the start of the ``IDAT`` chunk. All the chunks that + precede the ``IDAT`` chunk are read and either processed for + metadata or discarded. + """ + + self.validate_signature() + + while True: + if not self.atchunk: + self.atchunk = self.chunklentype() + if self.atchunk is None: + raise FormatError("This PNG file has no IDAT chunks.") + if self.atchunk[1] == "IDAT": + return + self.process_chunk() + + def chunklentype(self): + """Reads just enough of the input to determine the next + chunk's length and type, returned as a (*length*, *type*) pair + where *type* is a string. If there are no more chunks, ``None`` + is returned. + """ + + x = self.file.read(8) + if not x: + return None + if len(x) != 8: + raise FormatError("End of file whilst reading chunk length and type.") + length, type = struct.unpack("!I4s", x) + type = bytestostr(type) + if length > 2 ** 31 - 1: + raise FormatError("Chunk %s is too large: %d." % (type, length)) + return length, type + + def process_chunk(self): + """Process the next chunk and its data. This only processes the + following chunk types, all others are ignored: ``IHDR``, + ``PLTE``, ``bKGD``, ``tRNS``, ``gAMA``, ``sBIT``. + """ + + type, data = self.chunk() + if type == "IHDR": + # http://www.w3.org/TR/PNG/#11IHDR + if len(data) != 13: + raise FormatError("IHDR chunk has incorrect length.") + ( + self.width, + self.height, + self.bitdepth, + self.color_type, + self.compression, + self.filter, + self.interlace, + ) = struct.unpack("!2I5B", data) + + # Check that the header specifies only valid combinations. + if self.bitdepth not in (1, 2, 4, 8, 16): + raise Error("invalid bit depth %d" % self.bitdepth) + if self.color_type not in (0, 2, 3, 4, 6): + raise Error("invalid colour type %d" % self.color_type) + # Check indexed (palettized) images have 8 or fewer bits + # per pixel; check only indexed or greyscale images have + # fewer than 8 bits per pixel. + if (self.color_type & 1 and self.bitdepth > 8) or ( + self.bitdepth < 8 and self.color_type not in (0, 3) + ): + raise FormatError( + "Illegal combination of bit depth (%d)" + " and colour type (%d)." + " See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ." + % (self.bitdepth, self.color_type) + ) + if self.compression != 0: + raise Error("unknown compression method %d" % self.compression) + if self.filter != 0: + raise FormatError( + "Unknown filter method %d," + " see http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters ." + % self.filter + ) + if self.interlace not in (0, 1): + raise FormatError( + "Unknown interlace method %d," + " see http://www.w3.org/TR/2003/REC-PNG-20031110/#8InterlaceMethods ." + % self.interlace + ) + + # Derived values + # http://www.w3.org/TR/PNG/#6Colour-values + colormap = bool(self.color_type & 1) + greyscale = not (self.color_type & 2) + alpha = bool(self.color_type & 4) + color_planes = (3, 1)[greyscale or colormap] + planes = color_planes + alpha + + self.colormap = colormap + self.greyscale = greyscale + self.alpha = alpha + self.color_planes = color_planes + self.planes = planes + self.psize = float(self.bitdepth) / float(8) * planes + if int(self.psize) == self.psize: + self.psize = int(self.psize) + self.row_bytes = int(math.ceil(self.width * self.psize)) + # Stores PLTE chunk if present, and is used to check + # chunk ordering constraints. + self.plte = None + # Stores tRNS chunk if present, and is used to check chunk + # ordering constraints. + self.trns = None + # Stores sbit chunk if present. + self.sbit = None + elif type == "PLTE": + # http://www.w3.org/TR/PNG/#11PLTE + if self.plte: + warnings.warn("Multiple PLTE chunks present.") + self.plte = data + if len(data) % 3 != 0: + raise FormatError("PLTE chunk's length should be a multiple of 3.") + if len(data) > (2 ** self.bitdepth) * 3: + raise FormatError("PLTE chunk is too long.") + if len(data) == 0: + raise FormatError("Empty PLTE is not allowed.") + elif type == "bKGD": + try: + if self.colormap: + if not self.plte: + warnings.warn("PLTE chunk is required before bKGD chunk.") + self.background = struct.unpack("B", data) + else: + self.background = struct.unpack("!%dH" % self.color_planes, data) + except struct.error: + raise FormatError("bKGD chunk has incorrect length.") + elif type == "tRNS": + # http://www.w3.org/TR/PNG/#11tRNS + self.trns = data + if self.colormap: + if not self.plte: + warnings.warn("PLTE chunk is required before tRNS chunk.") + else: + if len(data) > len(self.plte) / 3: + # Was warning, but promoted to Error as it + # would otherwise cause pain later on. + raise FormatError("tRNS chunk is too long.") + else: + if self.alpha: + raise FormatError( + "tRNS chunk is not valid with colour type %d." % self.color_type + ) + try: + self.transparent = struct.unpack("!%dH" % self.color_planes, data) + except struct.error: + raise FormatError("tRNS chunk has incorrect length.") + elif type == "gAMA": + try: + self.gamma = struct.unpack("!L", data)[0] / 100000.0 + except struct.error: + raise FormatError("gAMA chunk has incorrect length.") + elif type == "sBIT": + self.sbit = data + if ( + self.colormap + and len(data) != 3 + or not self.colormap + and len(data) != self.planes + ): + raise FormatError("sBIT chunk has incorrect length.") + + def read(self): + """ + Read the PNG file and decode it. Returns (`width`, `height`, + `pixels`, `metadata`). + + May use excessive memory. + + `pixels` are returned in boxed row flat pixel format. + """ + + def iteridat(): + """Iterator that yields all the ``IDAT`` chunks as strings.""" + while True: + try: + type, data = self.chunk() + except ValueError: + e = geterror() + raise ChunkError(e.args[0]) + if type == "IEND": + # http://www.w3.org/TR/PNG/#11IEND + break + if type != "IDAT": + continue + # type == 'IDAT' + # http://www.w3.org/TR/PNG/#11IDAT + if self.colormap and not self.plte: + warnings.warn("PLTE chunk is required before IDAT chunk") + yield data + + def iterdecomp(idat): + """Iterator that yields decompressed strings. `idat` should + be an iterator that yields the ``IDAT`` chunk data. + """ + + # Currently, with no max_length parameter to decompress, this + # routine will do one yield per IDAT chunk. So not very + # incremental. + d = zlib.decompressobj() + # Each IDAT chunk is passed to the decompressor, then any + # remaining state is decompressed out. + for data in idat: + # :todo: add a max_length argument here to limit output + # size. + yield array("B", d.decompress(data)) + yield array("B", d.flush()) + + self.preamble() + raw = iterdecomp(iteridat()) + + if self.interlace: + raw = array("B", itertools.chain(*raw)) + arraycode = "BH"[self.bitdepth > 8] + # Like :meth:`group` but producing an array.array object for + # each row. + pixels = imap_( + lambda *row: array(arraycode, row), + *[iter(self.deinterlace(raw))] * self.width * self.planes + ) + else: + pixels = self.iterboxed(self.iterstraight(raw)) + meta = dict() + for attr in "greyscale alpha planes bitdepth interlace".split(): + meta[attr] = getattr(self, attr) + meta["size"] = (self.width, self.height) + for attr in "gamma transparent background".split(): + a = getattr(self, attr, None) + if a is not None: + meta[attr] = a + return self.width, self.height, pixels, meta + + def read_flat(self): + """ + Read a PNG file and decode it into flat row flat pixel format. + Returns (*width*, *height*, *pixels*, *metadata*). + + May use excessive memory. + + `pixels` are returned in flat row flat pixel format. + + See also the :meth:`read` method which returns pixels in the + more stream-friendly boxed row flat pixel format. + """ + + x, y, pixel, meta = self.read() + arraycode = "BH"[meta["bitdepth"] > 8] + pixel = array(arraycode, itertools.chain(*pixel)) + return x, y, pixel, meta + + def palette(self, alpha="natural"): + """Returns a palette that is a sequence of 3-tuples or 4-tuples, + synthesizing it from the ``PLTE`` and ``tRNS`` chunks. These + chunks should have already been processed (for example, by + calling the :meth:`preamble` method). All the tuples are the + same size: 3-tuples if there is no ``tRNS`` chunk, 4-tuples when + there is a ``tRNS`` chunk. Assumes that the image is colour type + 3 and therefore a ``PLTE`` chunk is required. + + If the `alpha` argument is ``'force'`` then an alpha channel is + always added, forcing the result to be a sequence of 4-tuples. + """ + + if not self.plte: + raise FormatError("Required PLTE chunk is missing in colour type 3 image.") + plte = group(array("B", self.plte), 3) + if self.trns or alpha == "force": + trns = array("B", self.trns or "") + trns.extend([255] * (len(plte) - len(trns))) + plte = map(operator.add, plte, group(trns, 1)) + return plte + + def asDirect(self): + """Returns the image data as a direct representation of an + ``x * y * planes`` array. This method is intended to remove the + need for callers to deal with palettes and transparency + themselves. Images with a palette (colour type 3) + are converted to RGB or RGBA; images with transparency (a + ``tRNS`` chunk) are converted to LA or RGBA as appropriate. + When returned in this format the pixel values represent the + colour value directly without needing to refer to palettes or + transparency information. + + Like the :meth:`read` method this method returns a 4-tuple: + + (*width*, *height*, *pixels*, *meta*) + + This method normally returns pixel values with the bit depth + they have in the source image, but when the source PNG has an + ``sBIT`` chunk it is inspected and can reduce the bit depth of + the result pixels; pixel values will be reduced according to + the bit depth specified in the ``sBIT`` chunk (PNG nerds should + note a single result bit depth is used for all channels; the + maximum of the ones specified in the ``sBIT`` chunk. An RGB565 + image will be rescaled to 6-bit RGB666). + + The *meta* dictionary that is returned reflects the `direct` + format and not the original source image. For example, an RGB + source image with a ``tRNS`` chunk to represent a transparent + colour, will have ``planes=3`` and ``alpha=False`` for the + source image, but the *meta* dictionary returned by this method + will have ``planes=4`` and ``alpha=True`` because an alpha + channel is synthesized and added. + + *pixels* is the pixel data in boxed row flat pixel format (just + like the :meth:`read` method). + + All the other aspects of the image data are not changed. + """ + + self.preamble() + + # Simple case, no conversion necessary. + if not self.colormap and not self.trns and not self.sbit: + return self.read() + + x, y, pixels, meta = self.read() + + if self.colormap: + meta["colormap"] = False + meta["alpha"] = bool(self.trns) + meta["bitdepth"] = 8 + meta["planes"] = 3 + bool(self.trns) + plte = self.palette() + + def iterpal(pixels): + for row in pixels: + row = map(plte.__getitem__, row) + yield array("B", itertools.chain(*row)) + + pixels = iterpal(pixels) + elif self.trns: + # It would be nice if there was some reasonable way of doing + # this without generating a whole load of intermediate tuples. + # But tuples does seem like the easiest way, with no other way + # clearly much simpler or much faster. (Actually, the L to LA + # conversion could perhaps go faster (all those 1-tuples!), but + # I still wonder whether the code proliferation is worth it) + it = self.transparent + maxval = 2 ** meta["bitdepth"] - 1 + planes = meta["planes"] + meta["alpha"] = True + meta["planes"] += 1 + typecode = "BH"[meta["bitdepth"] > 8] + + def itertrns(pixels): + for row in pixels: + # For each row we group it into pixels, then form a + # characterisation vector that says whether each pixel + # is opaque or not. Then we convert True/False to + # 0/maxval (by multiplication), and add it as the extra + # channel. + row = group(row, planes) + opa = map(it.__ne__, row) + opa = map(maxval.__mul__, opa) + opa = zip(opa) # convert to 1-tuples + yield array(typecode, itertools.chain(*map(operator.add, row, opa))) + + pixels = itertrns(pixels) + targetbitdepth = None + if self.sbit: + sbit = struct.unpack("%dB" % len(self.sbit), self.sbit) + targetbitdepth = max(sbit) + if targetbitdepth > meta["bitdepth"]: + raise Error("sBIT chunk %r exceeds bitdepth %d" % (sbit, self.bitdepth)) + if min(sbit) <= 0: + raise Error("sBIT chunk %r has a 0-entry" % sbit) + if targetbitdepth == meta["bitdepth"]: + targetbitdepth = None + if targetbitdepth: + shift = meta["bitdepth"] - targetbitdepth + meta["bitdepth"] = targetbitdepth + + def itershift(pixels): + for row in pixels: + yield map(shift.__rrshift__, row) + + pixels = itershift(pixels) + return x, y, pixels, meta + + def asFloat(self, maxval=1.0): + """Return image pixels as per :meth:`asDirect` method, but scale + all pixel values to be floating point values between 0.0 and + *maxval*. + """ + + x, y, pixels, info = self.asDirect() + sourcemaxval = 2 ** info["bitdepth"] - 1 + del info["bitdepth"] + info["maxval"] = float(maxval) + factor = float(maxval) / float(sourcemaxval) + + def iterfloat(): + for row in pixels: + yield map(factor.__mul__, row) + + return x, y, iterfloat(), info + + def _as_rescale(self, get, targetbitdepth): + """Helper used by :meth:`asRGB8` and :meth:`asRGBA8`.""" + + width, height, pixels, meta = get() + maxval = 2 ** meta["bitdepth"] - 1 + targetmaxval = 2 ** targetbitdepth - 1 + factor = float(targetmaxval) / float(maxval) + meta["bitdepth"] = targetbitdepth + + def iterscale(): + for row in pixels: + yield map(lambda x: int(round(x * factor)), row) + + return width, height, iterscale(), meta + + def asRGB8(self): + """Return the image data as an RGB pixels with 8-bits per + sample. This is like the :meth:`asRGB` method except that + this method additionally rescales the values so that they + are all between 0 and 255 (8-bit). In the case where the + source image has a bit depth < 8 the transformation preserves + all the information; where the source image has bit depth + > 8, then rescaling to 8-bit values loses precision. No + dithering is performed. Like :meth:`asRGB`, an alpha channel + in the source image will raise an exception. + + This function returns a 4-tuple: + (*width*, *height*, *pixels*, *metadata*). + *width*, *height*, *metadata* are as per the :meth:`read` method. + + *pixels* is the pixel data in boxed row flat pixel format. + """ + + return self._as_rescale(self.asRGB, 8) + + def asRGBA8(self): + """Return the image data as RGBA pixels with 8-bits per + sample. This method is similar to :meth:`asRGB8` and + :meth:`asRGBA`: The result pixels have an alpha channel, *and* + values are rescaled to the range 0 to 255. The alpha channel is + synthesized if necessary (with a small speed penalty). + """ + + return self._as_rescale(self.asRGBA, 8) + + def asRGB(self): + """Return image as RGB pixels. RGB colour images are passed + through unchanged; greyscales are expanded into RGB + triplets (there is a small speed overhead for doing this). + + An alpha channel in the source image will raise an + exception. + + The return values are as for the :meth:`read` method + except that the *metadata* reflect the returned pixels, not the + source image. In particular, for this method + ``metadata['greyscale']`` will be ``False``. + """ + + width, height, pixels, meta = self.asDirect() + if meta["alpha"]: + raise Error("will not convert image with alpha channel to RGB") + if not meta["greyscale"]: + return width, height, pixels, meta + meta["greyscale"] = False + typecode = "BH"[meta["bitdepth"] > 8] + + def iterrgb(): + for row in pixels: + a = array(typecode, [0]) * 3 * width + for i in range(3): + a[i::3] = row + yield a + + return width, height, iterrgb(), meta + + def asRGBA(self): + """Return image as RGBA pixels. Greyscales are expanded into + RGB triplets; an alpha channel is synthesized if necessary. + The return values are as for the :meth:`read` method + except that the *metadata* reflect the returned pixels, not the + source image. In particular, for this method + ``metadata['greyscale']`` will be ``False``, and + ``metadata['alpha']`` will be ``True``. + """ + + width, height, pixels, meta = self.asDirect() + if meta["alpha"] and not meta["greyscale"]: + return width, height, pixels, meta + typecode = "BH"[meta["bitdepth"] > 8] + maxval = 2 ** meta["bitdepth"] - 1 + + def newarray(): + return array(typecode, [0]) * 4 * width + + if meta["alpha"] and meta["greyscale"]: + # LA to RGBA + def convert(): + for row in pixels: + # Create a fresh target row, then copy L channel + # into first three target channels, and A channel + # into fourth channel. + a = newarray() + for i in range(3): + a[i::4] = row[0::2] + a[3::4] = row[1::2] + yield a + + elif meta["greyscale"]: + # L to RGBA + def convert(): + for row in pixels: + a = newarray() + for i in range(3): + a[i::4] = row + a[3::4] = array(typecode, [maxval]) * width + yield a + + else: + assert not meta["alpha"] and not meta["greyscale"] + # RGB to RGBA + def convert(): + for row in pixels: + a = newarray() + for i in range(3): + a[i::4] = row[i::3] + a[3::4] = array(typecode, [maxval]) * width + yield a + + meta["alpha"] = True + meta["greyscale"] = False + return width, height, convert(), meta + + +# === Internal Test Support === + +# This section comprises the tests that are internally validated (as +# opposed to tests which produce output files that are externally +# validated). Primarily they are unittests. + +# Note that it is difficult to internally validate the results of +# writing a PNG file. The only thing we can do is read it back in +# again, which merely checks consistency, not that the PNG file we +# produce is valid. + +# Run the tests from the command line: +# python -c 'import png;png.test()' + +# (For an in-memory binary file IO object) We use BytesIO where +# available, otherwise we use StringIO, but name it BytesIO. +try: + from io import BytesIO +except: + from StringIO import StringIO as BytesIO +import tempfile +import unittest + + +def test(): + unittest.main(__name__) + + +def topngbytes(name, rows, x, y, **k): + """Convenience function for creating a PNG file "in memory" as a + string. Creates a :class:`Writer` instance using the keyword arguments, + then passes `rows` to its :meth:`Writer.write` method. The resulting + PNG file is returned as a string. `name` is used to identify the file for + debugging. + """ + + import os + + print(name) + f = BytesIO() + w = Writer(x, y, **k) + w.write(f, rows) + if os.environ.get("PYPNG_TEST_TMP"): + w = open(name, "wb") + w.write(f.getvalue()) + w.close() + return f.getvalue() + + +def testWithIO(inp, out, f): + """Calls the function `f` with ``sys.stdin`` changed to `inp` + and ``sys.stdout`` changed to `out`. They are restored when `f` + returns. This function returns whatever `f` returns. + """ + + import os + + try: + oldin, sys.stdin = sys.stdin, inp + oldout, sys.stdout = sys.stdout, out + x = f() + finally: + sys.stdin = oldin + sys.stdout = oldout + if os.environ.get("PYPNG_TEST_TMP") and hasattr(out, "getvalue"): + name = mycallersname() + if name: + w = open(name + ".png", "wb") + w.write(out.getvalue()) + w.close() + return x + + +def mycallersname(): + """Returns the name of the caller of the caller of this function + (hence the name of the caller of the function in which + "mycallersname()" textually appears). Returns None if this cannot + be determined.""" + + # http://docs.python.org/library/inspect.html#the-interpreter-stack + import inspect + + frame = inspect.currentframe() + if not frame: + return None + frame_, filename_, lineno_, funname, linelist_, listi_ = inspect.getouterframes( + frame + )[2] + return funname + + +def seqtobytes(s): + """Convert a sequence of integers to a *bytes* instance. Good for + plastering over Python 2 / Python 3 cracks. + """ + + return strtobytes("".join(chr(x) for x in s)) + + +class Test(unittest.TestCase): + # This member is used by the superclass. If we don't define a new + # class here then when we use self.assertRaises() and the PyPNG code + # raises an assertion then we get no proper traceback. I can't work + # out why, but defining a new class here means we get a proper + # traceback. + class failureException(Exception): + pass + + def helperLN(self, n): + mask = (1 << n) - 1 + # Use small chunk_limit so that multiple chunk writing is + # tested. Making it a test for Issue 20. + w = Writer(15, 17, greyscale=True, bitdepth=n, chunk_limit=99) + f = BytesIO() + w.write_array(f, array("B", map(mask.__and__, range(1, 256)))) + r = Reader(bytes=f.getvalue()) + x, y, pixels, meta = r.read() + self.assertEqual(x, 15) + self.assertEqual(y, 17) + self.assertEqual( + list(itertools.chain(*pixels)), map(mask.__and__, range(1, 256)) + ) + + def testL8(self): + return self.helperLN(8) + + def testL4(self): + return self.helperLN(4) + + def testL2(self): + "Also tests asRGB8." + w = Writer(1, 4, greyscale=True, bitdepth=2) + f = BytesIO() + w.write_array(f, array("B", range(4))) + r = Reader(bytes=f.getvalue()) + x, y, pixels, meta = r.asRGB8() + self.assertEqual(x, 1) + self.assertEqual(y, 4) + for i, row in enumerate(pixels): + self.assertEqual(len(row), 3) + self.assertEqual(list(row), [0x55 * i] * 3) + + def testP2(self): + "2-bit palette." + a = (255, 255, 255) + b = (200, 120, 120) + c = (50, 99, 50) + w = Writer(1, 4, bitdepth=2, palette=[a, b, c]) + f = BytesIO() + w.write_array(f, array("B", (0, 1, 1, 2))) + r = Reader(bytes=f.getvalue()) + x, y, pixels, meta = r.asRGB8() + self.assertEqual(x, 1) + self.assertEqual(y, 4) + self.assertEqual(list(pixels), map(list, [a, b, b, c])) + + def testPtrns(self): + "Test colour type 3 and tRNS chunk (and 4-bit palette)." + a = (50, 99, 50, 50) + b = (200, 120, 120, 80) + c = (255, 255, 255) + d = (200, 120, 120) + e = (50, 99, 50) + w = Writer(3, 3, bitdepth=4, palette=[a, b, c, d, e]) + f = BytesIO() + w.write_array(f, array("B", (4, 3, 2, 3, 2, 0, 2, 0, 1))) + r = Reader(bytes=f.getvalue()) + x, y, pixels, meta = r.asRGBA8() + self.assertEqual(x, 3) + self.assertEqual(y, 3) + c = c + (255,) + d = d + (255,) + e = e + (255,) + boxed = [(e, d, c), (d, c, a), (c, a, b)] + flat = map(lambda row: itertools.chain(*row), boxed) + self.assertEqual(map(list, pixels), map(list, flat)) + + def testRGBtoRGBA(self): + "asRGBA8() on colour type 2 source." "" + # Test for Issue 26 + r = Reader(bytes=_pngsuite["basn2c08"]) + x, y, pixels, meta = r.asRGBA8() + # Test the pixels at row 9 columns 0 and 1. + row9 = list(pixels)[9] + self.assertEqual(row9[0:8], [0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xDE, 0xFF, 0xFF]) + + def testLtoRGBA(self): + "asRGBA() on grey source." "" + # Test for Issue 60 + r = Reader(bytes=_pngsuite["basi0g08"]) + x, y, pixels, meta = r.asRGBA() + row9 = list(list(pixels)[9]) + self.assertEqual(row9[0:8], [222, 222, 222, 255, 221, 221, 221, 255]) + + def testCtrns(self): + "Test colour type 2 and tRNS chunk." + # Test for Issue 25 + r = Reader(bytes=_pngsuite["tbrn2c08"]) + x, y, pixels, meta = r.asRGBA8() + # I just happen to know that the first pixel is transparent. + # In particular it should be #7f7f7f00 + row0 = list(pixels)[0] + self.assertEqual(tuple(row0[0:4]), (0x7F, 0x7F, 0x7F, 0x00)) + + def testAdam7read(self): + """Adam7 interlace reading. + Specifically, test that for images in the PngSuite that + have both an interlaced and straightlaced pair that both + images from the pair produce the same array of pixels.""" + for candidate in _pngsuite: + if not candidate.startswith("basn"): + continue + candi = candidate.replace("n", "i") + if candi not in _pngsuite: + continue + print("adam7 read %s" % (candidate,)) + straight = Reader(bytes=_pngsuite[candidate]) + adam7 = Reader(bytes=_pngsuite[candi]) + # Just compare the pixels. Ignore x,y (because they're + # likely to be correct?); metadata is ignored because the + # "interlace" member differs. Lame. + straight = straight.read()[2] + adam7 = adam7.read()[2] + self.assertEqual(map(list, straight), map(list, adam7)) + + def testAdam7write(self): + """Adam7 interlace writing. + For each test image in the PngSuite, write an interlaced + and a straightlaced version. Decode both, and compare results. + """ + # Not such a great test, because the only way we can check what + # we have written is to read it back again. + + for name, bytes in _pngsuite.items(): + # Only certain colour types supported for this test. + if name[3:5] not in ["n0", "n2", "n4", "n6"]: + continue + it = Reader(bytes=bytes) + x, y, pixels, meta = it.read() + pngi = topngbytes( + "adam7wn" + name + ".png", + pixels, + x=x, + y=y, + bitdepth=it.bitdepth, + greyscale=it.greyscale, + alpha=it.alpha, + transparent=it.transparent, + interlace=False, + ) + x, y, ps, meta = Reader(bytes=pngi).read() + it = Reader(bytes=bytes) + x, y, pixels, meta = it.read() + pngs = topngbytes( + "adam7wi" + name + ".png", + pixels, + x=x, + y=y, + bitdepth=it.bitdepth, + greyscale=it.greyscale, + alpha=it.alpha, + transparent=it.transparent, + interlace=True, + ) + x, y, pi, meta = Reader(bytes=pngs).read() + self.assertEqual(map(list, ps), map(list, pi)) + + def testPGMin(self): + """Test that the command line tool can read PGM files.""" + + def do(): + return _main(["testPGMin"]) + + s = BytesIO() + s.write(strtobytes("P5 2 2 3\n")) + s.write(strtobytes("\x00\x01\x02\x03")) + s.flush() + s.seek(0) + o = BytesIO() + testWithIO(s, o, do) + r = Reader(bytes=o.getvalue()) + x, y, pixels, meta = r.read() + self.assertTrue(r.greyscale) + self.assertEqual(r.bitdepth, 2) + + def testPAMin(self): + """Test that the command line tool can read PAM file.""" + + def do(): + return _main(["testPAMin"]) + + s = BytesIO() + s.write( + strtobytes( + "P7\nWIDTH 3\nHEIGHT 1\nDEPTH 4\nMAXVAL 255\n" + "TUPLTYPE RGB_ALPHA\nENDHDR\n" + ) + ) + # The pixels in flat row flat pixel format + flat = [255, 0, 0, 255, 0, 255, 0, 120, 0, 0, 255, 30] + asbytes = seqtobytes(flat) + s.write(asbytes) + s.flush() + s.seek(0) + o = BytesIO() + testWithIO(s, o, do) + r = Reader(bytes=o.getvalue()) + x, y, pixels, meta = r.read() + self.assertTrue(r.alpha) + self.assertTrue(not r.greyscale) + self.assertEqual(list(itertools.chain(*pixels)), flat) + + def testLA4(self): + """Create an LA image with bitdepth 4.""" + bytes = topngbytes( + "la4.png", [[5, 12]], 1, 1, greyscale=True, alpha=True, bitdepth=4 + ) + sbit = Reader(bytes=bytes).chunk("sBIT")[1] + self.assertEqual(sbit, strtobytes("\x04\x04")) + + def testPNMsbit(self): + """Test that PNM files can generates sBIT chunk.""" + + def do(): + return _main(["testPNMsbit"]) + + s = BytesIO() + s.write(strtobytes("P6 8 1 1\n")) + for pixel in range(8): + s.write(struct.pack(" 255: + a = array("H") + else: + a = array("B") + fw = float(width) + fh = float(height) + pfun = test_patterns[pattern] + for y in range(height): + fy = float(y) / fh + for x in range(width): + a.append(int(round(pfun(float(x) / fw, fy) * maxval))) + return a + + def test_rgba(size=256, bitdepth=8, red="GTB", green="GLR", blue="RTL", alpha=None): + """ + Create a test image. Each channel is generated from the + specified pattern; any channel apart from red can be set to + None, which will cause it not to be in the image. It + is possible to create all PNG channel types (L, RGB, LA, RGBA), + as well as non PNG channel types (RGA, and so on). + """ + + i = test_pattern(size, size, bitdepth, red) + psize = 1 + for channel in (green, blue, alpha): + if channel: + c = test_pattern(size, size, bitdepth, channel) + i = interleave_planes(i, c, psize, 1) + psize += 1 + return i + + def pngsuite_image(name): + """ + Create a test image by reading an internal copy of the files + from the PngSuite. Returned in flat row flat pixel format. + """ + + if name not in _pngsuite: + raise NotImplementedError( + "cannot find PngSuite file %s (use -L for a list)" % name + ) + r = Reader(bytes=_pngsuite[name]) + w, h, pixels, meta = r.asDirect() + assert w == h + # LAn for n < 8 is a special case for which we need to rescale + # the data. + if meta["greyscale"] and meta["alpha"] and meta["bitdepth"] < 8: + factor = 255 // (2 ** meta["bitdepth"] - 1) + + def rescale(data): + for row in data: + yield map(factor.__mul__, row) + + pixels = rescale(pixels) + meta["bitdepth"] = 8 + arraycode = "BH"[meta["bitdepth"] > 8] + return w, array(arraycode, itertools.chain(*pixels)), meta + + # The body of test_suite() + size = 256 + if options.test_size: + size = options.test_size + options.bitdepth = options.test_depth + options.greyscale = bool(options.test_black) + + kwargs = {} + if options.test_red: + kwargs["red"] = options.test_red + if options.test_green: + kwargs["green"] = options.test_green + if options.test_blue: + kwargs["blue"] = options.test_blue + if options.test_alpha: + kwargs["alpha"] = options.test_alpha + if options.greyscale: + if options.test_red or options.test_green or options.test_blue: + raise ValueError( + "cannot specify colours (R, G, B) when greyscale image (black channel, K) is specified" + ) + kwargs["red"] = options.test_black + kwargs["green"] = None + kwargs["blue"] = None + options.alpha = bool(options.test_alpha) + if not args: + pixels = test_rgba(size, options.bitdepth, **kwargs) + else: + size, pixels, meta = pngsuite_image(args[0]) + for k in ["bitdepth", "alpha", "greyscale"]: + setattr(options, k, meta[k]) + + writer = Writer( + size, + size, + bitdepth=options.bitdepth, + transparent=options.transparent, + background=options.background, + gamma=options.gamma, + greyscale=options.greyscale, + alpha=options.alpha, + compression=options.compression, + interlace=options.interlace, + ) + writer.write_array(sys.stdout, pixels) + + +def read_pam_header(infile): + """ + Read (the rest of a) PAM header. `infile` should be positioned + immediately after the initial 'P7' line (at the beginning of the + second line). Returns are as for `read_pnm_header`. + """ + + # Unlike PBM, PGM, and PPM, we can read the header a line at a time. + header = dict() + while True: + l = infile.readline().strip() + if l == strtobytes("ENDHDR"): + break + if not l: + raise EOFError("PAM ended prematurely") + if l[0] == strtobytes("#"): + continue + l = l.split(None, 1) + if l[0] not in header: + header[l[0]] = l[1] + else: + header[l[0]] += strtobytes(" ") + l[1] + + required = ["WIDTH", "HEIGHT", "DEPTH", "MAXVAL"] + required = [strtobytes(x) for x in required] + WIDTH, HEIGHT, DEPTH, MAXVAL = required + present = [x for x in required if x in header] + if len(present) != len(required): + raise Error("PAM file must specify WIDTH, HEIGHT, DEPTH, and MAXVAL") + width = int(header[WIDTH]) + height = int(header[HEIGHT]) + depth = int(header[DEPTH]) + maxval = int(header[MAXVAL]) + if width <= 0 or height <= 0 or depth <= 0 or maxval <= 0: + raise Error("WIDTH, HEIGHT, DEPTH, MAXVAL must all be positive integers") + return "P7", width, height, depth, maxval + + +def read_pnm_header(infile, supported=("P5", "P6")): + """ + Read a PNM header, returning (format,width,height,depth,maxval). + `width` and `height` are in pixels. `depth` is the number of + channels in the image; for PBM and PGM it is synthesized as 1, for + PPM as 3; for PAM images it is read from the header. `maxval` is + synthesized (as 1) for PBM images. + """ + + # Generally, see http://netpbm.sourceforge.net/doc/ppm.html + # and http://netpbm.sourceforge.net/doc/pam.html + + supported = [strtobytes(x) for x in supported] + + # Technically 'P7' must be followed by a newline, so by using + # rstrip() we are being liberal in what we accept. I think this + # is acceptable. + type = infile.read(3).rstrip() + if type not in supported: + raise NotImplementedError("file format %s not supported" % type) + if type == strtobytes("P7"): + # PAM header parsing is completely different. + return read_pam_header(infile) + # Expected number of tokens in header (3 for P4, 4 for P6) + expected = 4 + pbm = ("P1", "P4") + if type in pbm: + expected = 3 + header = [type] + + # We have to read the rest of the header byte by byte because the + # final whitespace character (immediately following the MAXVAL in + # the case of P6) may not be a newline. Of course all PNM files in + # the wild use a newline at this point, so it's tempting to use + # readline; but it would be wrong. + def getc(): + c = infile.read(1) + if not c: + raise Error("premature EOF reading PNM header") + return c + + c = getc() + while True: + # Skip whitespace that precedes a token. + while c.isspace(): + c = getc() + # Skip comments. + while c == "#": + while c not in "\n\r": + c = getc() + if not c.isdigit(): + raise Error("unexpected character %s found in header" % c) + # According to the specification it is legal to have comments + # that appear in the middle of a token. + # This is bonkers; I've never seen it; and it's a bit awkward to + # code good lexers in Python (no goto). So we break on such + # cases. + token = strtobytes("") + while c.isdigit(): + token += c + c = getc() + # Slight hack. All "tokens" are decimal integers, so convert + # them here. + header.append(int(token)) + if len(header) == expected: + break + # Skip comments (again) + while c == "#": + while c not in "\n\r": + c = getc() + if not c.isspace(): + raise Error("expected header to end with whitespace, not %s" % c) + + if type in pbm: + # synthesize a MAXVAL + header.append(1) + depth = (1, 3)[type == strtobytes("P6")] + return header[0], header[1], header[2], depth, header[3] + + +def write_pnm(file, width, height, pixels, meta): + """Write a Netpbm PNM/PAM file.""" + + bitdepth = meta["bitdepth"] + maxval = 2 ** bitdepth - 1 + # Rudely, the number of image planes can be used to determine + # whether we are L (PGM), LA (PAM), RGB (PPM), or RGBA (PAM). + planes = meta["planes"] + # Can be an assert as long as we assume that pixels and meta came + # from a PNG file. + assert planes in (1, 2, 3, 4) + if planes in (1, 3): + if 1 == planes: + # PGM + # Could generate PBM if maxval is 1, but we don't (for one + # thing, we'd have to convert the data, not just blat it + # out). + fmt = "P5" + else: + # PPM + fmt = "P6" + file.write("%s %d %d %d\n" % (fmt, width, height, maxval)) + if planes in (2, 4): + # PAM + # See http://netpbm.sourceforge.net/doc/pam.html + if 2 == planes: + tupltype = "GRAYSCALE_ALPHA" + else: + tupltype = "RGB_ALPHA" + file.write( + "P7\nWIDTH %d\nHEIGHT %d\nDEPTH %d\nMAXVAL %d\n" + "TUPLTYPE %s\nENDHDR\n" % (width, height, planes, maxval, tupltype) + ) + # Values per row + vpr = planes * width + # struct format + fmt = ">%d" % vpr + if maxval > 0xFF: + fmt = fmt + "H" + else: + fmt = fmt + "B" + for row in pixels: + file.write(struct.pack(fmt, *row)) + file.flush() + + +def color_triple(color): + """ + Convert a command line colour value to a RGB triple of integers. + FIXME: Somewhere we need support for greyscale backgrounds etc. + """ + if color.startswith("#") and len(color) == 4: + return (int(color[1], 16), int(color[2], 16), int(color[3], 16)) + if color.startswith("#") and len(color) == 7: + return (int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16)) + elif color.startswith("#") and len(color) == 13: + return (int(color[1:5], 16), int(color[5:9], 16), int(color[9:13], 16)) + + +def _main(argv): + """ + Run the PNG encoder with options from the command line. + """ + + # Parse command line arguments + from optparse import OptionParser + import re + + version = "%prog " + re.sub(r"( ?\$|URL: |Rev:)", "", __version__) + parser = OptionParser(version=version) + parser.set_usage("%prog [options] [imagefile]") + parser.add_option( + "-r", + "--read-png", + default=False, + action="store_true", + help="Read PNG, write PNM", + ) + parser.add_option( + "-i", + "--interlace", + default=False, + action="store_true", + help="create an interlaced PNG file (Adam7)", + ) + parser.add_option( + "-t", + "--transparent", + action="store", + type="string", + metavar="color", + help="mark the specified colour (#RRGGBB) as transparent", + ) + parser.add_option( + "-b", + "--background", + action="store", + type="string", + metavar="color", + help="save the specified background colour", + ) + parser.add_option( + "-a", + "--alpha", + action="store", + type="string", + metavar="pgmfile", + help="alpha channel transparency (RGBA)", + ) + parser.add_option( + "-g", + "--gamma", + action="store", + type="float", + metavar="value", + help="save the specified gamma value", + ) + parser.add_option( + "-c", + "--compression", + action="store", + type="int", + metavar="level", + help="zlib compression level (0-9)", + ) + parser.add_option( + "-T", + "--test", + default=False, + action="store_true", + help="create a test image (a named PngSuite image if an argument is supplied)", + ) + parser.add_option( + "-L", + "--list", + default=False, + action="store_true", + help="print list of named test images", + ) + parser.add_option( + "-R", + "--test-red", + action="store", + type="string", + metavar="pattern", + help="test pattern for the red image layer", + ) + parser.add_option( + "-G", + "--test-green", + action="store", + type="string", + metavar="pattern", + help="test pattern for the green image layer", + ) + parser.add_option( + "-B", + "--test-blue", + action="store", + type="string", + metavar="pattern", + help="test pattern for the blue image layer", + ) + parser.add_option( + "-A", + "--test-alpha", + action="store", + type="string", + metavar="pattern", + help="test pattern for the alpha image layer", + ) + parser.add_option( + "-K", + "--test-black", + action="store", + type="string", + metavar="pattern", + help="test pattern for greyscale image", + ) + parser.add_option( + "-d", + "--test-depth", + default=8, + action="store", + type="int", + metavar="NBITS", + help="create test PNGs that are NBITS bits per channel", + ) + parser.add_option( + "-S", + "--test-size", + action="store", + type="int", + metavar="size", + help="width and height of the test image", + ) + (options, args) = parser.parse_args(args=argv[1:]) + + # Convert options + if options.transparent is not None: + options.transparent = color_triple(options.transparent) + if options.background is not None: + options.background = color_triple(options.background) + + if options.list: + names = list(_pngsuite) + names.sort() + for name in names: + print(name) + return + + # Run regression tests + if options.test: + return test_suite(options, args) + + # Prepare input and output files + if len(args) == 0: + infilename = "-" + infile = sys.stdin + elif len(args) == 1: + infilename = args[0] + infile = open(infilename, "rb") + else: + parser.error("more than one input file") + outfile = sys.stdout + + if options.read_png: + # Encode PNG to PPM + png = Reader(file=infile) + width, height, pixels, meta = png.asDirect() + write_pnm(outfile, width, height, pixels, meta) + else: + # Encode PNM to PNG + format, width, height, depth, maxval = read_pnm_header( + infile, ("P5", "P6", "P7") + ) + # When it comes to the variety of input formats, we do something + # rather rude. Observe that L, LA, RGB, RGBA are the 4 colour + # types supported by PNG and that they correspond to 1, 2, 3, 4 + # channels respectively. So we use the number of channels in + # the source image to determine which one we have. We do not + # care about TUPLTYPE. + greyscale = depth <= 2 + pamalpha = depth in (2, 4) + supported = map(lambda x: 2 ** x - 1, range(1, 17)) + try: + mi = supported.index(maxval) + except ValueError: + raise NotImplementedError( + "your maxval (%s) not in supported list %s" % (maxval, str(supported)) + ) + bitdepth = mi + 1 + writer = Writer( + width, + height, + greyscale=greyscale, + bitdepth=bitdepth, + interlace=options.interlace, + transparent=options.transparent, + background=options.background, + alpha=bool(pamalpha or options.alpha), + gamma=options.gamma, + compression=options.compression, + ) + if options.alpha: + pgmfile = open(options.alpha, "rb") + format, awidth, aheight, adepth, amaxval = read_pnm_header(pgmfile, "P5") + if amaxval != "255": + raise NotImplementedError( + "maxval %s not supported for alpha channel" % amaxval + ) + if (awidth, aheight) != (width, height): + raise ValueError( + "alpha channel image size mismatch" + " (%s has %sx%s but %s has %sx%s)" + % (infilename, width, height, options.alpha, awidth, aheight) + ) + writer.convert_ppm_and_pgm(infile, pgmfile, outfile) + else: + writer.convert_pnm(infile, outfile) + + +if __name__ == "__main__": + try: + _main(sys.argv) + except Error: + e = geterror() + sys.stderr.write("%s\n" % (e,)) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/run_tests.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/run_tests.py new file mode 100644 index 0000000..8ef3db8 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/run_tests.py @@ -0,0 +1,355 @@ +import sys + +if __name__ == "__main__": + sys.exit("This module is for import only") + +test_pkg_name = ".".join(__name__.split(".")[0:-2]) +is_pygame_pkg = test_pkg_name == "pygame.tests" +test_runner_mod = test_pkg_name + ".test_utils.test_runner" + +if is_pygame_pkg: + from pygame.tests.test_utils import import_submodule + from pygame.tests.test_utils.test_runner import ( + prepare_test_env, + run_test, + combine_results, + get_test_results, + TEST_RESULTS_START, + ) +else: + from test.test_utils import import_submodule + from test.test_utils.test_runner import ( + prepare_test_env, + run_test, + combine_results, + get_test_results, + TEST_RESULTS_START, + ) +import pygame +import pygame.threads + +import os +import re +import shutil +import tempfile +import time +import random +from pprint import pformat + +was_run = False + + +def run(*args, **kwds): + """Run the Pygame unit test suite and return (total tests run, fails dict) + + Positional arguments (optional): + The names of tests to include. If omitted then all tests are run. Test + names need not include the trailing '_test'. + + Keyword arguments: + incomplete - fail incomplete tests (default False) + usesubprocess - run all test suites in the current process + (default False, use separate subprocesses) + dump - dump failures/errors as dict ready to eval (default False) + file - if provided, the name of a file into which to dump failures/errors + timings - if provided, the number of times to run each individual test to + get an average run time (default is run each test once) + exclude - A list of TAG names to exclude from the run. The items may be + comma or space separated. + show_output - show silenced stderr/stdout on errors (default False) + all - dump all results, not just errors (default False) + randomize - randomize order of tests (default False) + seed - if provided, a seed randomizer integer + multi_thread - if provided, the number of THREADS in which to run + subprocessed tests + time_out - if subprocess is True then the time limit in seconds before + killing a test (default 30) + fake - if provided, the name of the fake tests package in the + run_tests__tests subpackage to run instead of the normal + Pygame tests + python - the path to a python executable to run subprocessed tests + (default sys.executable) + interative - allow tests tagged 'interative'. + + Return value: + A tuple of total number of tests run, dictionary of error information. The + dictionary is empty if no errors were recorded. + + By default individual test modules are run in separate subprocesses. This + recreates normal Pygame usage where pygame.init() and pygame.quit() are + called only once per program execution, and avoids unfortunate + interactions between test modules. Also, a time limit is placed on test + execution, so frozen tests are killed when there time allotment expired. + Use the single process option if threading is not working properly or if + tests are taking too long. It is not guaranteed that all tests will pass + in single process mode. + + Tests are run in a randomized order if the randomize argument is True or a + seed argument is provided. If no seed integer is provided then the system + time is used. + + Individual test modules may have a corresponding *_tags.py module, + defining a __tags__ attribute, a list of tag strings used to selectively + omit modules from a run. By default only the 'interactive', 'ignore', and + 'subprocess_ignore' tags are ignored. 'interactive' is for modules that + take user input, like cdrom_test.py. 'ignore' and 'subprocess_ignore' for + for disabling modules for foreground and subprocess modes respectively. + These are for disabling tests on optional modules or for experimental + modules with known problems. These modules can be run from the console as + a Python program. + + This function can only be called once per Python session. It is not + reentrant. + + """ + + global was_run + + if was_run: + raise RuntimeError("run() was already called this session") + was_run = True + + options = kwds.copy() + option_usesubprocess = options.get("usesubprocess", False) + option_dump = options.pop("dump", False) + option_file = options.pop("file", None) + option_randomize = options.get("randomize", False) + option_seed = options.get("seed", None) + option_multi_thread = options.pop("multi_thread", 1) + option_time_out = options.pop("time_out", 120) + option_fake = options.pop("fake", None) + option_python = options.pop("python", sys.executable) + option_exclude = options.pop("exclude", ()) + option_interactive = options.pop("interactive", False) + + if not option_interactive and "interactive" not in option_exclude: + option_exclude += ("interactive",) + if option_usesubprocess and "subprocess_ignore" not in option_exclude: + option_exclude += ("subprocess_ignore",) + elif "ignore" not in option_exclude: + option_exclude += ("ignore",) + if sys.version_info < (3, 0, 0): + option_exclude += ("python2_ignore",) + else: + option_exclude += ("python3_ignore",) + + if pygame.get_sdl_version() < (2, 0, 0): + option_exclude += ("SDL1_ignore",) + else: + option_exclude += ("SDL2_ignore",) + main_dir, test_subdir, fake_test_subdir = prepare_test_env() + + ########################################################################### + # Compile a list of test modules. If fake, then compile list of fake + # xxxx_test.py from run_tests__tests + + TEST_MODULE_RE = re.compile(r"^(.+_test)\.py$") + + test_mods_pkg_name = test_pkg_name + + working_dir_temp = tempfile.mkdtemp() + + if option_fake is not None: + test_mods_pkg_name = ".".join( + [test_mods_pkg_name, "run_tests__tests", option_fake] + ) + test_subdir = os.path.join(fake_test_subdir, option_fake) + working_dir = test_subdir + else: + working_dir = working_dir_temp + + # Added in because some machines will need os.environ else there will be + # false failures in subprocess mode. Same issue as python2.6. Needs some + # env vars. + + test_env = os.environ + + fmt1 = "%s.%%s" % test_mods_pkg_name + fmt2 = "%s.%%s_test" % test_mods_pkg_name + if args: + test_modules = [m.endswith("_test") and (fmt1 % m) or (fmt2 % m) for m in args] + else: + test_modules = [] + for f in sorted(os.listdir(test_subdir)): + for match in TEST_MODULE_RE.findall(f): + test_modules.append(fmt1 % (match,)) + + ########################################################################### + # Remove modules to be excluded. + + tmp = test_modules + test_modules = [] + for name in tmp: + tag_module_name = "%s_tags" % (name[0:-5],) + try: + tag_module = import_submodule(tag_module_name) + except ImportError: + test_modules.append(name) + else: + try: + tags = tag_module.__tags__ + except AttributeError: + print("%s has no tags: ignoring" % (tag_module_name,)) + test_modules.append(name) + else: + for tag in tags: + if tag in option_exclude: + print("skipping %s (tag '%s')" % (name, tag)) + break + else: + test_modules.append(name) + del tmp, tag_module_name, name + + ########################################################################### + # Meta results + + results = {} + meta_results = {"__meta__": {}} + meta = meta_results["__meta__"] + + ########################################################################### + # Randomization + + if option_randomize or option_seed is not None: + if option_seed is None: + option_seed = time.time() + meta["random_seed"] = option_seed + print("\nRANDOM SEED USED: %s\n" % option_seed) + random.seed(option_seed) + random.shuffle(test_modules) + + ########################################################################### + # Single process mode + + if not option_usesubprocess: + options["exclude"] = option_exclude + t = time.time() + for module in test_modules: + results.update(run_test(module, **options)) + t = time.time() - t + + ########################################################################### + # Subprocess mode + # + + else: + if is_pygame_pkg: + from pygame.tests.test_utils.async_sub import proc_in_time_or_kill + else: + from test.test_utils.async_sub import proc_in_time_or_kill + + pass_on_args = ["--exclude", ",".join(option_exclude)] + for field in ["randomize", "incomplete", "unbuffered", "verbosity"]: + if kwds.get(field, False): + pass_on_args.append("--" + field) + + def sub_test(module): + print("loading %s" % module) + + cmd = [option_python, "-m", test_runner_mod, module] + pass_on_args + + return ( + module, + (cmd, test_env, working_dir), + proc_in_time_or_kill( + cmd, option_time_out, env=test_env, wd=working_dir + ), + ) + + if option_multi_thread > 1: + + def tmap(f, args): + return pygame.threads.tmap( + f, args, stop_on_error=False, num_workers=option_multi_thread + ) + + else: + tmap = map + + t = time.time() + + for module, cmd, (return_code, raw_return) in tmap(sub_test, test_modules): + test_file = "%s.py" % os.path.join(test_subdir, module) + cmd, test_env, working_dir = cmd + + test_results = get_test_results(raw_return) + if test_results: + results.update(test_results) + else: + results[module] = {} + + results[module].update( + dict( + return_code=return_code, + raw_return=raw_return, + cmd=cmd, + test_file=test_file, + test_env=test_env, + working_dir=working_dir, + module=module, + ) + ) + + t = time.time() - t + + ########################################################################### + # Output Results + # + + untrusty_total, combined = combine_results(results, t) + total, n_errors, n_failures = count_results(results) + + meta["total_tests"] = total + meta["combined"] = combined + meta["total_errors"] = n_errors + meta["total_failures"] = n_failures + results.update(meta_results) + + if not option_usesubprocess and total != untrusty_total: + raise AssertionError( + "Something went wrong in the Test Machinery:\n" + "total: %d != untrusty_total: %d" % (total, untrusty_total) + ) + + if not option_dump: + print(combined) + else: + print(TEST_RESULTS_START) + print(pformat(results)) + + if option_file is not None: + results_file = open(option_file, "w") + try: + results_file.write(pformat(results)) + finally: + results_file.close() + + shutil.rmtree(working_dir_temp) + + return total, n_errors + n_failures + + +def count_results(results): + total = errors = failures = 0 + for result in results.values(): + if result.get("return_code", 0): + total += 1 + errors += 1 + else: + total += result["num_tests"] + errors += result["num_errors"] + failures += result["num_failures"] + + return total, errors, failures + + +def run_and_exit(*args, **kwargs): + """Run the tests, and if there are failures, exit with a return code of 1. + + This is needed for various buildbots to recognise that the tests have + failed. + """ + total, fails = run(*args, **kwargs) + if fails: + sys.exit(1) + sys.exit(0) diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/test_machinery.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/test_machinery.py new file mode 100644 index 0000000..114c281 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/test_machinery.py @@ -0,0 +1,89 @@ +import inspect +import random +import re +import unittest + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +from . import import_submodule + + +class PygameTestLoader(unittest.TestLoader): + def __init__( + self, randomize_tests=False, include_incomplete=False, exclude=("interactive",) + ): + super(PygameTestLoader, self).__init__() + self.randomize_tests = randomize_tests + + if exclude is None: + self.exclude = set() + else: + self.exclude = set(exclude) + + if include_incomplete: + self.testMethodPrefix = ("test", "todo_") + + def getTestCaseNames(self, testCaseClass): + res = [] + for name in super(PygameTestLoader, self).getTestCaseNames(testCaseClass): + tags = get_tags(testCaseClass, getattr(testCaseClass, name)) + if self.exclude.isdisjoint(tags): + res.append(name) + + if self.randomize_tests: + random.shuffle(res) + + return res + + +# Exclude by tags: + +TAGS_RE = re.compile(r"\|[tT]ags:(-?[ a-zA-Z,0-9_\n]+)\|", re.M) + + +class TestTags: + def __init__(self): + self.memoized = {} + self.parent_modules = {} + + def get_parent_module(self, class_): + if class_ not in self.parent_modules: + self.parent_modules[class_] = import_submodule(class_.__module__) + return self.parent_modules[class_] + + def __call__(self, parent_class, meth): + key = (parent_class, meth.__name__) + if key not in self.memoized: + parent_module = self.get_parent_module(parent_class) + + module_tags = getattr(parent_module, "__tags__", []) + class_tags = getattr(parent_class, "__tags__", []) + + tags = TAGS_RE.search(inspect.getdoc(meth) or "") + if tags: + test_tags = [t.strip() for t in tags.group(1).split(",")] + else: + test_tags = [] + + combined = set() + for tags in (module_tags, class_tags, test_tags): + if not tags: + continue + + add = set([t for t in tags if not t.startswith("-")]) + remove = set([t[1:] for t in tags if t not in add]) + + if add: + combined.update(add) + if remove: + combined.difference_update(remove) + + self.memoized[key] = combined + + return self.memoized[key] + + +get_tags = TestTags() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/test_runner.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/test_runner.py new file mode 100644 index 0000000..0433975 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/test_utils/test_runner.py @@ -0,0 +1,333 @@ +import sys +import os + +if __name__ == "__main__": + pkg_dir = os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest +from .test_machinery import PygameTestLoader + +import re + +try: + import StringIO +except ImportError: + import io as StringIO + +import optparse +from pprint import pformat + + +def prepare_test_env(): + test_subdir = os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + main_dir = os.path.split(test_subdir)[0] + sys.path.insert(0, test_subdir) + fake_test_subdir = os.path.join(test_subdir, "run_tests__tests") + return main_dir, test_subdir, fake_test_subdir + + +main_dir, test_subdir, fake_test_subdir = prepare_test_env() + +################################################################################ +# Set the command line options +# +# options are shared with run_tests.py so make sure not to conflict +# in time more will be added here + +TAG_PAT = r"-?[a-zA-Z0-9_]+" +TAG_RE = re.compile(TAG_PAT) +EXCLUDE_RE = re.compile(r"(%s,?\s*)+$" % (TAG_PAT,)) + + +def exclude_callback(option, opt, value, parser): + if EXCLUDE_RE.match(value) is None: + raise optparse.OptionValueError("%s argument has invalid value" % (opt,)) + parser.values.exclude = TAG_RE.findall(value) + + +opt_parser = optparse.OptionParser() + +opt_parser.add_option( + "-i", "--incomplete", action="store_true", help="fail incomplete tests" +) + +opt_parser.add_option( + "-s", + "--usesubprocess", + action="store_true", + help="run everything in a single process " " (default: use no subprocesses)", +) + +opt_parser.add_option( + "-e", + "--exclude", + action="callback", + type="string", + help="exclude tests containing any of TAGS", + callback=exclude_callback, +) + +opt_parser.add_option( + "-u", + "--unbuffered", + action="store_true", + help="Show stdout/stderr as tests run, rather than storing it and showing on failures", +) + +opt_parser.add_option( + "-v", + "--verbose", + dest="verbosity", + action="store_const", + const=2, + help="Verbose output", +) +opt_parser.add_option( + "-q", + "--quiet", + dest="verbosity", + action="store_const", + const=0, + help="Quiet output", +) + +opt_parser.add_option( + "-r", "--randomize", action="store_true", help="randomize order of tests" +) + +################################################################################ +# If an xxxx_test.py takes longer than TIME_OUT seconds it will be killed +# This is only the default, can be over-ridden on command line + +TIME_OUT = 30 + +# DEFAULTS + +################################################################################ +# Human readable output +# + +COMPLETE_FAILURE_TEMPLATE = """ +====================================================================== +ERROR: all_tests_for (%(module)s.AllTestCases) +---------------------------------------------------------------------- +Traceback (most recent call last): + File "test/%(module)s.py", line 1, in all_tests_for +subprocess completely failed with return code of %(return_code)s +cmd: %(cmd)s +test_env: %(test_env)s +working_dir: %(working_dir)s +return (first 10 and last 10 lines): +%(raw_return)s + +""" # Leave that last empty line else build page regex won't match +# Text also needs to be vertically compressed + + +RAN_TESTS_DIV = (70 * "-") + "\nRan" + +DOTS = re.compile("^([FE.sux]*)$", re.MULTILINE) + + +def extract_tracebacks(output): + """ from test runner output return the tracebacks. + """ + verbose_mode = " ..." in output + + if verbose_mode: + if "ERROR" in output or "FAILURE" in output: + return "\n\n==".join(output.split("\n\n==")[1:]) + else: + dots = DOTS.search(output).group(1) + if "E" in dots or "F" in dots: + return output[len(dots) + 1 :].split(RAN_TESTS_DIV)[0] + return "" + + +def output_into_dots(output): + """ convert the test runner output into dots. + """ + # verbose_mode = ") ..." in output + verbose_mode = " ..." in output + + if verbose_mode: + # a map from the verbose output to the dots output. + reasons = { + "... ERROR": "E", + "... unexpected success": "u", + "... skipped": "s", + "... expected failure": "x", + "... ok": ".", + "... FAIL": "F", + } + results = output.split("\n\n==")[0] + lines = [l for l in results.split("\n") if l and "..." in l] + dotlist = [] + for l in lines: + found = False + for reason in reasons: + if reason in l: + dotlist.append(reasons[reason]) + found = True + break + if not found: + raise ValueError("Not sure what this is. Add to reasons. :%s" % l) + + return "".join(dotlist) + dots = DOTS.search(output).group(1) + return dots + + +def combine_results(all_results, t): + """ + + Return pieced together results in a form fit for human consumption. Don't + rely on results if piecing together subprocessed results (single process + mode is fine). Was originally meant for that purpose but was found to be + unreliable. See the dump option for reliable results. + + """ + + all_dots = "" + failures = [] + + for module, results in sorted(all_results.items()): + output, return_code, raw_return = map( + results.get, ("output", "return_code", "raw_return") + ) + + if not output or (return_code and RAN_TESTS_DIV not in output): + # would this effect the original dict? TODO + output_lines = raw_return.splitlines() + if len(output_lines) > 20: + results["raw_return"] = "\n".join( + output_lines[:10] + ["..."] + output_lines[-10:] + ) + failures.append(COMPLETE_FAILURE_TEMPLATE % results) + all_dots += "E" + continue + + dots = output_into_dots(output) + all_dots += dots + tracebacks = extract_tracebacks(output) + if tracebacks: + failures.append(tracebacks) + + total_fails, total_errors = map(all_dots.count, "FE") + total_tests = len(all_dots) + + combined = [all_dots] + if failures: + combined += ["".join(failures).lstrip("\n")[:-1]] + combined += ["%s %s tests in %.3fs\n" % (RAN_TESTS_DIV, total_tests, t)] + + if failures: + infos = (["failures=%s" % total_fails] if total_fails else []) + ( + ["errors=%s" % total_errors] if total_errors else [] + ) + combined += ["FAILED (%s)\n" % ", ".join(infos)] + else: + combined += ["OK\n"] + + return total_tests, "\n".join(combined) + + +################################################################################ + +TEST_RESULTS_START = "<--!! TEST RESULTS START HERE !!-->" +TEST_RESULTS_END = "<--!! TEST RESULTS END HERE !!-->" +_test_re_str = "%s\n(.*)%s" % (TEST_RESULTS_START, TEST_RESULTS_END) +TEST_RESULTS_RE = re.compile(_test_re_str, re.DOTALL | re.M) + + +def get_test_results(raw_return): + test_results = TEST_RESULTS_RE.search(raw_return) + if test_results: + try: + return eval(test_results.group(1)) + except: + print("BUGGY TEST RESULTS EVAL:\n %s" % test_results.group(1)) + raise + + +################################################################################ + + +def run_test( + module, + incomplete=False, + usesubprocess=True, + randomize=False, + exclude=("interactive",), + buffer=True, + unbuffered=None, + verbosity=1, +): + """Run a unit test module + """ + suite = unittest.TestSuite() + + if verbosity is None: + verbosity = 1 + + if verbosity: + print("loading %s" % module) + + loader = PygameTestLoader( + randomize_tests=randomize, include_incomplete=incomplete, exclude=exclude + ) + suite.addTest(loader.loadTestsFromName(module)) + + output = StringIO.StringIO() + runner = unittest.TextTestRunner(stream=output, buffer=buffer, verbosity=verbosity) + results = runner.run(suite) + + if verbosity == 2: + output.seek(0) + print(output.read()) + output.seek(0) + + results = { + module: { + "output": output.getvalue(), + "num_tests": results.testsRun, + "num_errors": len(results.errors), + "num_failures": len(results.failures), + } + } + + if usesubprocess: + print(TEST_RESULTS_START) + print(pformat(results)) + print(TEST_RESULTS_END) + else: + return results + + +################################################################################ + +if __name__ == "__main__": + options, args = opt_parser.parse_args() + if not args: + + if is_pygame_pkg: + run_from = "pygame.tests.go" + else: + run_from = os.path.join(main_dir, "run_tests.py") + sys.exit("No test module provided; consider using %s instead" % run_from) + run_test( + args[0], + incomplete=options.incomplete, + usesubprocess=options.usesubprocess, + randomize=options.randomize, + exclude=options.exclude, + buffer=(not options.unbuffered), + ) + +################################################################################ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/threads_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/threads_test.py new file mode 100644 index 0000000..300f6a9 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/threads_test.py @@ -0,0 +1,177 @@ +import unittest +from pygame.threads import FuncResult, tmap, WorkerQueue, Empty, STOP +from pygame import threads +from pygame.compat import xrange_ + +import time + + +class WorkerQueueTypeTest(unittest.TestCase): + def test_usage_with_different_functions(self): + def f(x): + return x + 1 + + def f2(x): + return x + 2 + + wq = WorkerQueue() + fr = FuncResult(f) + fr2 = FuncResult(f2) + wq.do(fr, 1) + wq.do(fr2, 1) + wq.wait() + wq.stop() + + self.assertEqual(fr.result, 2) + self.assertEqual(fr2.result, 3) + + def todo_test_do(self): + + # __doc__ (as of 2008-06-28) for pygame.threads.WorkerQueue.do: + + # puts a function on a queue for running later. + # + + self.fail() + + def test_stop(self): + """Ensure stop() stops the worker queue""" + wq = WorkerQueue() + + self.assertGreater(len(wq.pool), 0) + + for t in wq.pool: + self.assertTrue(t.isAlive()) + + for i in xrange_(200): + wq.do(lambda x: x + 1, i) + + wq.stop() + + for t in wq.pool: + self.assertFalse(t.isAlive()) + + self.assertIs(wq.queue.get(), STOP) + + def todo_test_threadloop(self): + + # __doc__ (as of 2008-06-28) for pygame.threads.WorkerQueue.threadloop: + + # Loops until all of the tasks are finished. + + self.fail() + + def test_wait(self): + + # __doc__ (as of 2008-06-28) for pygame.threads.WorkerQueue.wait: + + # waits until all tasks are complete. + + wq = WorkerQueue() + + for i in xrange_(2000): + wq.do(lambda x: x + 1, i) + wq.wait() + + self.assertRaises(Empty, wq.queue.get_nowait) + + wq.stop() + + +class ThreadsModuleTest(unittest.TestCase): + def todo_test_benchmark_workers(self): + "tags:long_running" + + # __doc__ (as of 2008-06-28) for pygame.threads.benchmark_workers: + + # does a little test to see if workers are at all faster. + # Returns the number of workers which works best. + # Takes a little bit of time to run, so you should only really call + # it once. + # You can pass in benchmark data, and functions if you want. + # a_bench_func - f(data) + # the_data - data to work on. + + self.fail() + + def test_init(self): + """Ensure init() sets up the worker queue""" + threads.init(8) + + self.assertIsInstance(threads._wq, WorkerQueue) + + threads.quit() + + def test_quit(self): + """Ensure quit() cleans up the worker queue""" + threads.init(8) + threads.quit() + + self.assertIsNone(threads._wq) + + def test_tmap(self): + # __doc__ (as of 2008-06-28) for pygame.threads.tmap: + + # like map, but uses a thread pool to execute. + # num_workers - the number of worker threads that will be used. If pool + # is passed in, then the num_workers arg is ignored. + # worker_queue - you can optionally pass in an existing WorkerQueue. + # wait - True means that the results are returned when everything is finished. + # False means that we return the [worker_queue, results] right away instead. + # results, is returned as a list of FuncResult instances. + # stop_on_error - + + func, data = lambda x: x + 1, xrange_(100) + + tmapped = list(tmap(func, data)) + mapped = list(map(func, data)) + + self.assertEqual(tmapped, mapped) + + def todo_test_tmap__None_func_and_multiple_sequences(self): + """Using a None as func and multiple sequences""" + self.fail() + + res = tmap(None, [1, 2, 3, 4]) + res2 = tmap(None, [1, 2, 3, 4], [22, 33, 44, 55]) + res3 = tmap(None, [1, 2, 3, 4], [22, 33, 44, 55, 66]) + res4 = tmap(None, [1, 2, 3, 4, 5], [22, 33, 44, 55]) + + self.assertEqual([1, 2, 3, 4], res) + self.assertEqual([(1, 22), (2, 33), (3, 44), (4, 55)], res2) + self.assertEqual([(1, 22), (2, 33), (3, 44), (4, 55), (None, 66)], res3) + self.assertEqual([(1, 22), (2, 33), (3, 44), (4, 55), (5, None)], res4) + + def test_tmap__wait(self): + r = range(1000) + wq, results = tmap(lambda x: x, r, num_workers=5, wait=False) + wq.wait() + r2 = map(lambda x: x.result, results) + self.assertEqual(list(r), list(r2)) + + def test_FuncResult(self): + """Ensure FuncResult sets its result and exception attributes""" + # Results are stored in result attribute + fr = FuncResult(lambda x: x + 1) + fr(2) + + self.assertEqual(fr.result, 3) + + # Exceptions are store in exception attribute + self.assertIsNone(fr.exception, "no exception should be raised") + + exception = ValueError("rast") + + def x(sdf): + raise exception + + fr = FuncResult(x) + fr(None) + + self.assertIs(fr.exception, exception) + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/time_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/time_test.py new file mode 100644 index 0000000..44c4e52 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/time_test.py @@ -0,0 +1,205 @@ +import unittest +import pygame + +Clock = pygame.time.Clock + + +class ClockTypeTest(unittest.TestCase): + def test_construction(self): + """Ensure a Clock object can be created""" + c = Clock() + + self.assertTrue(c, "Clock cannot be constructed") + + def todo_test_get_fps(self): + + # __doc__ (as of 2008-08-02) for pygame.time.Clock.get_fps: + + # Clock.get_fps(): return float + # compute the clock framerate + # + # Compute your game's framerate (in frames per second). It is computed + # by averaging the last few calls to Clock.tick(). + # + + self.fail() + + # delay_per_frame = 1 / 100.0 + # + # c = Clock() + # + # for f in range(100): + # c.tick() + # time.sleep(delay_per_frame) + # + # self.assertTrue(99.0 < c.get_fps() < 101.0) + + def todo_test_get_rawtime(self): + + # __doc__ (as of 2008-08-02) for pygame.time.Clock.get_rawtime: + + # Clock.get_rawtime(): return milliseconds + # actual time used in the previous tick + # + # Similar to Clock.get_time(), but this does not include any time used + # while Clock.tick() was delaying to limit the framerate. + # + + self.fail() + + def todo_test_get_time(self): + + # __doc__ (as of 2008-08-02) for pygame.time.Clock.get_time: + + # Clock.get_time(): return milliseconds + # time used in the previous tick + # + # Returns the parameter passed to the last call to Clock.tick(). It is + # the number of milliseconds passed between the previous two calls to + # Pygame.tick(). + # + + self.fail() + + # c = Clock() + # c.tick() #between here + # time.sleep(0.02) + # #get_time() + # c.tick() # here + # + # time.sleep(0.02) + # + # self.assertTrue(20 <= c.get_time() <= 30) + + def todo_test_tick(self): + + # __doc__ (as of 2008-08-02) for pygame.time.Clock.tick: + + # Clock.tick(framerate=0): return milliseconds + # control timer events + # update the clock + # + # This method should be called once per frame. It will compute how + # many milliseconds have passed since the previous call. + # + # If you pass the optional framerate argument the function will delay + # to keep the game running slower than the given ticks per second. + # This can be used to help limit the runtime speed of a game. By + # calling Clock.tick(40) once per frame, the program will never run at + # more than 40 frames per second. + # + # Note that this function uses SDL_Delay function which is not + # accurate on every platform, but does not use much cpu. Use + # tick_busy_loop if you want an accurate timer, and don't mind chewing + # cpu. + # + + self.fail() + + # collection = [] + # c = Clock() + # + # c.tick() + # for i in range(100): + # time.sleep(0.005) + # collection.append(c.tick()) + # + # for outlier in [min(collection), max(collection)]: + # if outlier != 5: collection.remove(outlier) + # + # self.assertEqual(sum(collection) / len(collection), 5) + + def todo_test_tick_busy_loop(self): + + # __doc__ (as of 2008-08-02) for pygame.time.Clock.tick_busy_loop: + + # Clock.tick_busy_loop(framerate=0): return milliseconds + # control timer events + # update the clock + # + # This method should be called once per frame. It will compute how + # many milliseconds have passed since the previous call. + # + # If you pass the optional framerate argument the function will delay + # to keep the game running slower than the given ticks per second. + # This can be used to help limit the runtime speed of a game. By + # calling Clock.tick(40) once per frame, the program will never run at + # more than 40 frames per second. + # + # Note that this function uses pygame.time.delay, which uses lots of + # cpu in a busy loop to make sure that timing is more accurate. + # + # New in pygame 1.8.0. + + self.fail() + + +class TimeModuleTest(unittest.TestCase): + def todo_test_delay(self): + + # __doc__ (as of 2008-08-02) for pygame.time.delay: + + # pygame.time.delay(milliseconds): return time + # pause the program for an amount of time + # + # Will pause for a given number of milliseconds. This function will + # use the processor (rather than sleeping) in order to make the delay + # more accurate than pygame.time.wait(). + # + # This returns the actual number of milliseconds used. + + self.fail() + + def todo_test_get_ticks(self): + + # __doc__ (as of 2008-08-02) for pygame.time.get_ticks: + + # pygame.time.get_ticks(): return milliseconds + # get the time in milliseconds + # + # Return the number of milliseconds since pygame.init() was called. + # Before pygame is initialized this will always be 0. + # + + self.fail() + + def todo_test_set_timer(self): + + # __doc__ (as of 2008-08-02) for pygame.time.set_timer: + + # pygame.time.set_timer(eventid, milliseconds): return None + # repeatedly create an event on the event queue + # + # Set an event type to appear on the event queue every given number of + # milliseconds. The first event will not appear until the amount of + # time has passed. + # + # Every event type can have a separate timer attached to it. It is + # best to use the value between pygame.USEREVENT and pygame.NUMEVENTS. + # + # To disable the timer for an event, set the milliseconds argument to 0. + + self.fail() + + def todo_test_wait(self): + + # __doc__ (as of 2008-08-02) for pygame.time.wait: + + # pygame.time.wait(milliseconds): return time + # pause the program for an amount of time + # + # Will pause for a given number of milliseconds. This function sleeps + # the process to share the processor with other programs. A program + # that waits for even a few milliseconds will consume very little + # processor time. It is slightly less accurate than the + # pygame.time.delay() function. + # + # This returns the actual number of milliseconds used. + + self.fail() + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/touch_tags.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/touch_tags.py new file mode 100644 index 0000000..0135fc4 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/touch_tags.py @@ -0,0 +1 @@ +__tags__ = ["SDL1_ignore"] diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/touch_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/touch_test.py new file mode 100644 index 0000000..507c4f6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/touch_test.py @@ -0,0 +1,43 @@ +import unittest +import pygame +from pygame._sdl2 import touch + + +has_touchdevice = touch.get_num_devices() > 0 + + +class TouchTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + pygame.display.init() + + @classmethod + def tearDownClass(cls): + pygame.display.quit() + + def test_num_devices(self): + touch.get_num_devices() + + @unittest.skipIf(not has_touchdevice, "no touch devices found") + def test_get_device(self): + touch.get_device(0) + + def test_num_fingers__invalid(self): + self.assertRaises(pygame.error, touch.get_device, -1234) + self.assertRaises(TypeError, touch.get_device, "test") + + @unittest.skipIf(not has_touchdevice, "no touch devices found") + def test_num_fingers(self): + touch.get_num_fingers(touch.get_device(0)) + + def test_num_fingers__invalid(self): + self.assertRaises(TypeError, touch.get_num_fingers, "test") + self.assertRaises(pygame.error, touch.get_num_fingers, -1234) + + @unittest.skipIf(not has_touchdevice, "no touch devices found") + def todo_test_get_finger(self): + """ask for touch input and check the dict""" + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/transform_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/transform_test.py new file mode 100644 index 0000000..9a46784 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/transform_test.py @@ -0,0 +1,1152 @@ +import unittest +import platform + +from pygame.tests import test_utils +import pygame +import pygame.transform +from pygame.locals import * + + +def show_image(s, images=[]): + # pygame.display.init() + size = s.get_rect()[2:] + screen = pygame.display.set_mode(size) + screen.blit(s, (0, 0)) + pygame.display.flip() + pygame.event.pump() + going = True + idx = 0 + while going: + events = pygame.event.get() + for e in events: + if e.type == QUIT: + going = False + if e.type == KEYDOWN: + if e.key in [K_s, K_a]: + if e.key == K_s: + idx += 1 + if e.key == K_a: + idx -= 1 + s = images[idx] + screen.blit(s, (0, 0)) + pygame.display.flip() + pygame.event.pump() + elif e.key in [K_ESCAPE]: + going = False + pygame.display.quit() + pygame.display.init() + + +def threshold( + return_surf, + surf, + color, + threshold=(0, 0, 0), + diff_color=(0, 0, 0), + change_return=True, +): + """ given the color it makes return_surf only have areas with the given colour. + """ + + width, height = surf.get_width(), surf.get_height() + + if change_return: + return_surf.fill(diff_color) + + try: + r, g, b = color + except ValueError: + r, g, b, a = color + + try: + tr, tg, tb = color + except ValueError: + tr, tg, tb, ta = color + + similar = 0 + for y in xrange(height): + for x in xrange(width): + c1 = surf.get_at((x, y)) + + if (abs(c1[0] - r) < tr) & (abs(c1[1] - g) < tg) & (abs(c1[2] - b) < tb): + # this pixel is within the threshold. + if change_return: + return_surf.set_at((x, y), c1) + similar += 1 + # else: + # print c1, c2 + + return similar + + +class TransformModuleTest(unittest.TestCase): + def test_scale__alpha(self): + """ see if set_alpha information is kept. + """ + + s = pygame.Surface((32, 32)) + s.set_alpha(55) + self.assertEqual(s.get_alpha(), 55) + + s = pygame.Surface((32, 32)) + s.set_alpha(55) + s2 = pygame.transform.scale(s, (64, 64)) + s3 = s.copy() + self.assertEqual(s.get_alpha(), s3.get_alpha()) + self.assertEqual(s.get_alpha(), s2.get_alpha()) + + def test_scale__destination(self): + """ see if the destination surface can be passed in to use. + """ + + s = pygame.Surface((32, 32)) + s2 = pygame.transform.scale(s, (64, 64)) + s3 = s2.copy() + + s3 = pygame.transform.scale(s, (64, 64), s3) + pygame.transform.scale(s, (64, 64), s2) + + # the wrong size surface is past in. Should raise an error. + self.assertRaises(ValueError, pygame.transform.scale, s, (33, 64), s3) + + s = pygame.Surface((32, 32)) + s2 = pygame.transform.smoothscale(s, (64, 64)) + s3 = s2.copy() + + s3 = pygame.transform.smoothscale(s, (64, 64), s3) + pygame.transform.smoothscale(s, (64, 64), s2) + + # the wrong size surface is past in. Should raise an error. + self.assertRaises(ValueError, pygame.transform.smoothscale, s, (33, 64), s3) + + def test_scale__zero_surface_transform(self): + tmp_surface = pygame.transform.scale(pygame.Surface((128, 128)), (0, 0)) + self.assertEqual(tmp_surface.get_size(), (0, 0)) + tmp_surface = pygame.transform.scale(tmp_surface, (128, 128)) + self.assertEqual(tmp_surface.get_size(), (128, 128)) + + def test_threshold__honors_third_surface(self): + # __doc__ for threshold as of Tue 07/15/2008 + + # pygame.transform.threshold(DestSurface, Surface, color, threshold = + # (0,0,0,0), diff_color = (0,0,0,0), change_return = True, Surface = + # None): return num_threshold_pixels + + # When given the optional third + # surface, it would use the colors in that rather than the "color" + # specified in the function to check against. + + # New in pygame 1.8 + + ################################################################ + # Sizes + (w, h) = size = (32, 32) + + # the original_color is within the threshold of the threshold_color + threshold = (20, 20, 20, 20) + + original_color = (25, 25, 25, 25) + threshold_color = (10, 10, 10, 10) + + # Surfaces + original_surface = pygame.Surface(size, pygame.SRCALPHA, 32) + dest_surface = pygame.Surface(size, pygame.SRCALPHA, 32) + + # Third surface is used in lieu of 3rd position arg color + third_surface = pygame.Surface(size, pygame.SRCALPHA, 32) + + # Color filling + original_surface.fill(original_color) + third_surface.fill(threshold_color) + + ################################################################ + # All pixels for color should be within threshold + # + pixels_within_threshold = pygame.transform.threshold( + dest_surf=None, + surf=original_surface, + search_color=threshold_color, + threshold=threshold, + set_color=None, + set_behavior=0, + ) + + self.assertEqual(w * h, pixels_within_threshold) + + ################################################################ + # This should respect third_surface colors in place of 3rd arg + # color Should be the same as: surface.fill(threshold_color) + # all within threshold + + pixels_within_threshold = pygame.transform.threshold( + dest_surf=None, + surf=original_surface, + search_color=None, + threshold=threshold, + set_color=None, + set_behavior=0, + search_surf=third_surface, + ) + self.assertEqual(w * h, pixels_within_threshold) + + def test_threshold_dest_surf_not_change(self): + """ the pixels within the threshold. + + All pixels not within threshold are changed to set_color. + So there should be none changed in this test. + """ + (w, h) = size = (32, 32) + threshold = (20, 20, 20, 20) + original_color = (25, 25, 25, 25) + original_dest_color = (65, 65, 65, 55) + threshold_color = (10, 10, 10, 10) + set_color = (255, 10, 10, 10) + + surf = pygame.Surface(size, pygame.SRCALPHA, 32) + dest_surf = pygame.Surface(size, pygame.SRCALPHA, 32) + search_surf = pygame.Surface(size, pygame.SRCALPHA, 32) + + surf.fill(original_color) + search_surf.fill(threshold_color) + dest_surf.fill(original_dest_color) + + # set_behavior=1, set dest_surface from set_color. + # all within threshold of third_surface, so no color is set. + + THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR = 1 + pixels_within_threshold = pygame.transform.threshold( + dest_surf=dest_surf, + surf=surf, + search_color=None, + threshold=threshold, + set_color=set_color, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR, + search_surf=search_surf, + ) + + # # Return, of pixels within threshold is correct + self.assertEqual(w * h, pixels_within_threshold) + + # # Size of dest surface is correct + dest_rect = dest_surf.get_rect() + dest_size = dest_rect.size + self.assertEqual(size, dest_size) + + # The color is not the change_color specified for every pixel As all + # pixels are within threshold + + for pt in test_utils.rect_area_pts(dest_rect): + self.assertNotEqual(dest_surf.get_at(pt), set_color) + self.assertEqual(dest_surf.get_at(pt), original_dest_color) + + def test_threshold_dest_surf_all_changed(self): + """ Lowering the threshold, expecting changed surface + """ + + (w, h) = size = (32, 32) + threshold = (20, 20, 20, 20) + original_color = (25, 25, 25, 25) + original_dest_color = (65, 65, 65, 55) + threshold_color = (10, 10, 10, 10) + set_color = (255, 10, 10, 10) + + surf = pygame.Surface(size, pygame.SRCALPHA, 32) + dest_surf = pygame.Surface(size, pygame.SRCALPHA, 32) + search_surf = pygame.Surface(size, pygame.SRCALPHA, 32) + + surf.fill(original_color) + search_surf.fill(threshold_color) + dest_surf.fill(original_dest_color) + + THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR = 1 + pixels_within_threshold = pygame.transform.threshold( + dest_surf, + surf, + search_color=None, + set_color=set_color, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR, + search_surf=search_surf, + ) + + self.assertEqual(0, pixels_within_threshold) + + dest_rect = dest_surf.get_rect() + dest_size = dest_rect.size + self.assertEqual(size, dest_size) + + # The color is the set_color specified for every pixel As all + # pixels are not within threshold + for pt in test_utils.rect_area_pts(dest_rect): + self.assertEqual(dest_surf.get_at(pt), set_color) + + def test_threshold_count(self): + """ counts the colors, and not changes them. + """ + surf_size = (32, 32) + surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) + search_surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) + search_color = (55, 55, 55, 255) + original_color = (10, 10, 10, 255) + + surf.fill(original_color) + # set 2 pixels to the color we are searching for. + surf.set_at((0, 0), search_color) + surf.set_at((12, 5), search_color) + + # There is no destination surface, but we ask to change it. + # This should be an error. + self.assertRaises( + TypeError, pygame.transform.threshold, None, surf, search_color + ) + # from pygame.transform import THRESHOLD_BEHAVIOR_COUNT + THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF = 2 + self.assertRaises( + TypeError, + pygame.transform.threshold, + None, + surf, + search_color, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + ) + + THRESHOLD_BEHAVIOR_COUNT = 0 + num_threshold_pixels = pygame.transform.threshold( + dest_surf=None, + surf=surf, + search_color=search_color, + set_behavior=THRESHOLD_BEHAVIOR_COUNT, + ) + self.assertEqual(num_threshold_pixels, 2) + + def test_threshold_search_surf(self): + surf_size = (32, 32) + surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) + search_surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) + dest_surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) + + original_color = (10, 10, 10, 255) + search_color = (55, 55, 55, 255) + + surf.fill(original_color) + dest_surf.fill(original_color) + # set 2 pixels to the color we are searching for. + surf.set_at((0, 0), search_color) + surf.set_at((12, 5), search_color) + + search_surf.fill(search_color) + + # We look in the other surface for matching colors. + # Change it in dest_surf + THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF = 2 + + # TypeError: if search_surf is used, search_color should be None + self.assertRaises( + TypeError, + pygame.transform.threshold, + dest_surf, + surf, + search_color, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + search_surf=search_surf, + ) + + # surf, dest_surf, and search_surf should all be the same size. + # Check surface sizes are the same size. + different_sized_surf = pygame.Surface((22, 33), pygame.SRCALPHA, 32) + self.assertRaises( + TypeError, + pygame.transform.threshold, + different_sized_surf, + surf, + search_color=None, + set_color=None, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + search_surf=search_surf, + ) + + self.assertRaises( + TypeError, + pygame.transform.threshold, + dest_surf, + surf, + search_color=None, + set_color=None, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + search_surf=different_sized_surf, + ) + + # We look to see if colors in search_surf are in surf. + num_threshold_pixels = pygame.transform.threshold( + dest_surf=dest_surf, + surf=surf, + search_color=None, + set_color=None, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + search_surf=search_surf, + ) + + num_pixels_within = 2 + self.assertEqual(num_threshold_pixels, num_pixels_within) + + dest_surf.fill(original_color) + num_threshold_pixels = pygame.transform.threshold( + dest_surf, + surf, + search_color=None, + set_color=None, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + search_surf=search_surf, + inverse_set=True, + ) + + self.assertEqual(num_threshold_pixels, 2) + + def test_threshold_inverse_set(self): + """ changes the pixels within the threshold, and not outside. + """ + surf_size = (32, 32) + _dest_surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) + _surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) + + dest_surf = _dest_surf # surface we are changing. + surf = _surf # surface we are looking at + search_color = (55, 55, 55, 255) # color we are searching for. + threshold = (0, 0, 0, 0) # within this distance from search_color. + set_color = (245, 245, 245, 255) # color we set. + inverse_set = 1 # pixels within threshold are changed to 'set_color' + + original_color = (10, 10, 10, 255) + surf.fill(original_color) + # set 2 pixels to the color we are searching for. + surf.set_at((0, 0), search_color) + surf.set_at((12, 5), search_color) + + dest_surf.fill(original_color) + # set 2 pixels to the color we are searching for. + dest_surf.set_at((0, 0), search_color) + dest_surf.set_at((12, 5), search_color) + + THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR = 1 + num_threshold_pixels = pygame.transform.threshold( + dest_surf, + surf, + search_color=search_color, + threshold=threshold, + set_color=set_color, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR, + inverse_set=1, + ) + + self.assertEqual(num_threshold_pixels, 2) + # only two pixels changed to diff_color. + self.assertEqual(dest_surf.get_at((0, 0)), set_color) + self.assertEqual(dest_surf.get_at((12, 5)), set_color) + + # other pixels should be the same as they were before. + # We just check one other pixel, not all of them. + self.assertEqual(dest_surf.get_at((2, 2)), original_color) + + # XXX + def test_threshold_non_src_alpha(self): + + result = pygame.Surface((10, 10)) + s1 = pygame.Surface((10, 10)) + s2 = pygame.Surface((10, 10)) + s3 = pygame.Surface((10, 10)) + s4 = pygame.Surface((10, 10)) + + x = s1.fill((0, 0, 0)) + s1.set_at((0, 0), (32, 20, 0)) + + x = s2.fill((0, 20, 0)) + x = s3.fill((0, 0, 0)) + x = s4.fill((0, 0, 0)) + s2.set_at((0, 0), (33, 21, 0)) + s2.set_at((3, 0), (63, 61, 0)) + s3.set_at((0, 0), (112, 31, 0)) + s4.set_at((0, 0), (11, 31, 0)) + s4.set_at((1, 1), (12, 31, 0)) + + self.assertEqual(s1.get_at((0, 0)), (32, 20, 0, 255)) + self.assertEqual(s2.get_at((0, 0)), (33, 21, 0, 255)) + self.assertEqual((0, 0), (s1.get_flags(), s2.get_flags())) + + similar_color = (255, 255, 255, 255) + diff_color = (222, 0, 0, 255) + threshold_color = (20, 20, 20, 255) + + THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR = 1 + num_threshold_pixels = pygame.transform.threshold( + dest_surf=result, + surf=s1, + search_color=similar_color, + threshold=threshold_color, + set_color=diff_color, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR, + ) + self.assertEqual(num_threshold_pixels, 0) + + num_threshold_pixels = pygame.transform.threshold( + dest_surf=result, + surf=s1, + search_color=(40, 40, 0), + threshold=threshold_color, + set_color=diff_color, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR, + ) + self.assertEqual(num_threshold_pixels, 1) + + self.assertEqual(result.get_at((0, 0)), diff_color) + + def test_threshold__uneven_colors(self): + (w, h) = size = (16, 16) + + original_surface = pygame.Surface(size, pygame.SRCALPHA, 32) + dest_surface = pygame.Surface(size, pygame.SRCALPHA, 32) + + original_surface.fill(0) + + threshold_color_template = [5, 5, 5, 5] + threshold_template = [6, 6, 6, 6] + + ################################################################ + + for pos in range(len("rgb")): + threshold_color = threshold_color_template[:] + threshold = threshold_template[:] + + threshold_color[pos] = 45 + threshold[pos] = 50 + + pixels_within_threshold = pygame.transform.threshold( + None, + original_surface, + threshold_color, + threshold, + set_color=None, + set_behavior=0, + ) + + self.assertEqual(w * h, pixels_within_threshold) + + ################################################################ + + def test_threshold_set_behavior2(self): + """ raises an error when set_behavior=2 and set_color is not None. + """ + from pygame.transform import threshold + + s1 = pygame.Surface((32, 32), SRCALPHA, 32) + s2 = pygame.Surface((32, 32), SRCALPHA, 32) + THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF = 2 + self.assertRaises( + TypeError, + threshold, + dest_surf=s2, + surf=s1, + search_color=(30, 30, 30), + threshold=(11, 11, 11), + set_color=(255, 0, 0), + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + ) + + def test_threshold_set_behavior0(self): + """ raises an error when set_behavior=1 + and set_color is not None, + and dest_surf is not None. + """ + from pygame.transform import threshold + + s1 = pygame.Surface((32, 32), SRCALPHA, 32) + s2 = pygame.Surface((32, 32), SRCALPHA, 32) + THRESHOLD_BEHAVIOR_COUNT = 0 + + self.assertRaises( + TypeError, + threshold, + dest_surf=None, + surf=s2, + search_color=(30, 30, 30), + threshold=(11, 11, 11), + set_color=(0, 0, 0), + set_behavior=THRESHOLD_BEHAVIOR_COUNT, + ) + + self.assertRaises( + TypeError, + threshold, + dest_surf=s1, + surf=s2, + search_color=(30, 30, 30), + threshold=(11, 11, 11), + set_color=None, + set_behavior=THRESHOLD_BEHAVIOR_COUNT, + ) + + threshold( + dest_surf=None, + surf=s2, + search_color=(30, 30, 30), + threshold=(11, 11, 11), + set_color=None, + set_behavior=THRESHOLD_BEHAVIOR_COUNT, + ) + + def test_threshold_from_surface(self): + """ Set similar pixels in 'dest_surf' to color in the 'surf'. + """ + from pygame.transform import threshold + + surf = pygame.Surface((32, 32), SRCALPHA, 32) + dest_surf = pygame.Surface((32, 32), SRCALPHA, 32) + surf_color = (40, 40, 40, 255) + dest_color = (255, 255, 255) + surf.fill(surf_color) + dest_surf.fill(dest_color) + THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF = 2 + + num_threshold_pixels = threshold( + dest_surf=dest_surf, + surf=surf, + search_color=(30, 30, 30), + threshold=(11, 11, 11), + set_color=None, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + inverse_set=1, + ) + + self.assertEqual( + num_threshold_pixels, dest_surf.get_height() * dest_surf.get_width() + ) + self.assertEqual(dest_surf.get_at((0, 0)), surf_color) + + def test_threshold__surface(self): + """ + """ + from pygame.transform import threshold + + s1 = pygame.Surface((32, 32), SRCALPHA, 32) + s2 = pygame.Surface((32, 32), SRCALPHA, 32) + s3 = pygame.Surface((1, 1), SRCALPHA, 32) + THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF = 2 + + # # only one pixel should not be changed. + # s1.fill((40,40,40)) + # s2.fill((255,255,255)) + # s1.set_at( (0,0), (170, 170, 170) ) + # # set the similar pixels in destination surface to the color + # # in the first surface. + # num_threshold_pixels = threshold( + # dest_surf=s2, + # surf=s1, + # search_color=(30,30,30), + # threshold=(11,11,11), + # set_color=None, + # set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF) + + # #num_threshold_pixels = threshold(s2, s1, (30,30,30)) + # self.assertEqual(num_threshold_pixels, (s1.get_height() * s1.get_width()) -1) + # self.assertEqual(s2.get_at((0,0)), (0,0,0, 255)) + # self.assertEqual(s2.get_at((0,1)), (40, 40, 40, 255)) + # self.assertEqual(s2.get_at((17,1)), (40, 40, 40, 255)) + + # # abs(40 - 255) < 100 + # #(abs(c1[0] - r) < tr) + + # s1.fill((160,160,160)) + # s2.fill((255,255,255)) + # num_threshold_pixels = threshold(s2, s1, (255,255,255), (100,100,100), (0,0,0), True) + + # self.assertEqual(num_threshold_pixels, (s1.get_height() * s1.get_width())) + + # only one pixel should not be changed. + s1.fill((40, 40, 40)) + s1.set_at((0, 0), (170, 170, 170)) + THRESHOLD_BEHAVIOR_COUNT = 0 + + num_threshold_pixels = threshold( + dest_surf=None, + surf=s1, + search_color=(30, 30, 30), + threshold=(11, 11, 11), + set_color=None, + set_behavior=THRESHOLD_BEHAVIOR_COUNT, + ) + + # num_threshold_pixels = threshold(s2, s1, (30,30,30)) + self.assertEqual(num_threshold_pixels, (s1.get_height() * s1.get_width()) - 1) + + # test end markers. 0, and 255 + + # the pixels are different by 1. + s1.fill((254, 254, 254)) + s2.fill((255, 255, 255)) + s3.fill((255, 255, 255)) + s1.set_at((0, 0), (170, 170, 170)) + num_threshold_pixels = threshold( + None, s1, (254, 254, 254), (1, 1, 1), None, THRESHOLD_BEHAVIOR_COUNT + ) + self.assertEqual(num_threshold_pixels, (s1.get_height() * s1.get_width()) - 1) + + # compare the two surfaces. Should be all but one matching. + num_threshold_pixels = threshold( + None, s1, None, (1, 1, 1), None, THRESHOLD_BEHAVIOR_COUNT, s2 + ) + self.assertEqual(num_threshold_pixels, (s1.get_height() * s1.get_width()) - 1) + + # within (0,0,0) threshold? Should match no pixels. + num_threshold_pixels = threshold( + None, s1, (253, 253, 253), (0, 0, 0), None, THRESHOLD_BEHAVIOR_COUNT + ) + self.assertEqual(num_threshold_pixels, 0) + + # other surface within (0,0,0) threshold? Should match no pixels. + num_threshold_pixels = threshold( + None, s1, None, (0, 0, 0), None, THRESHOLD_BEHAVIOR_COUNT, s2 + ) + self.assertEqual(num_threshold_pixels, 0) + + def test_threshold__subclassed_surface(self): + """Ensure threshold accepts subclassed surfaces.""" + expected_size = (13, 11) + expected_flags = 0 + expected_depth = 32 + expected_color = (90, 80, 70, 255) + expected_count = 0 + surface = test_utils.SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) + dest_surface = test_utils.SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) + search_surface = test_utils.SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) + surface.fill((10, 10, 10)) + dest_surface.fill((255, 255, 255)) + search_surface.fill((20, 20, 20)) + + count = pygame.transform.threshold( + dest_surf=dest_surface, + surf=surface, + threshold=(1, 1, 1), + set_color=expected_color, + search_color=None, + search_surf=search_surface, + ) + + self.assertIsInstance(dest_surface, pygame.Surface) + self.assertIsInstance(dest_surface, test_utils.SurfaceSubclass) + self.assertEqual(count, expected_count) + self.assertEqual(dest_surface.get_at((0, 0)), expected_color) + self.assertEqual(dest_surface.get_bitsize(), expected_depth) + self.assertEqual(dest_surface.get_size(), expected_size) + self.assertEqual(dest_surface.get_flags(), expected_flags) + + def test_laplacian(self): + """ + """ + + SIZE = 32 + s1 = pygame.Surface((SIZE, SIZE)) + s2 = pygame.Surface((SIZE, SIZE)) + s1.fill((10, 10, 70)) + pygame.draw.line(s1, (255, 0, 0), (3, 10), (20, 20)) + + # a line at the last row of the image. + pygame.draw.line(s1, (255, 0, 0), (0, 31), (31, 31)) + + pygame.transform.laplacian(s1, s2) + + # show_image(s1) + # show_image(s2) + + self.assertEqual(s2.get_at((0, 0)), (0, 0, 0, 255)) + self.assertEqual(s2.get_at((3, 10)), (255, 0, 0, 255)) + self.assertEqual(s2.get_at((0, 31)), (255, 0, 0, 255)) + self.assertEqual(s2.get_at((31, 31)), (255, 0, 0, 255)) + + # here we create the return surface. + s2 = pygame.transform.laplacian(s1) + + self.assertEqual(s2.get_at((0, 0)), (0, 0, 0, 255)) + self.assertEqual(s2.get_at((3, 10)), (255, 0, 0, 255)) + self.assertEqual(s2.get_at((0, 31)), (255, 0, 0, 255)) + self.assertEqual(s2.get_at((31, 31)), (255, 0, 0, 255)) + + def test_average_surfaces(self): + """ + """ + + SIZE = 32 + s1 = pygame.Surface((SIZE, SIZE)) + s2 = pygame.Surface((SIZE, SIZE)) + s3 = pygame.Surface((SIZE, SIZE)) + s1.fill((10, 10, 70)) + s2.fill((10, 20, 70)) + s3.fill((10, 130, 10)) + + surfaces = [s1, s2, s3] + surfaces = [s1, s2] + sr = pygame.transform.average_surfaces(surfaces) + + self.assertEqual(sr.get_at((0, 0)), (10, 15, 70, 255)) + + self.assertRaises(TypeError, pygame.transform.average_surfaces, 1) + self.assertRaises(TypeError, pygame.transform.average_surfaces, []) + + self.assertRaises(TypeError, pygame.transform.average_surfaces, [1]) + self.assertRaises(TypeError, pygame.transform.average_surfaces, [s1, 1]) + self.assertRaises(TypeError, pygame.transform.average_surfaces, [1, s1]) + self.assertRaises(TypeError, pygame.transform.average_surfaces, [s1, s2, 1]) + + self.assertRaises( + TypeError, pygame.transform.average_surfaces, (s for s in [s1, s2, s3]) + ) + + def test_average_surfaces__24(self): + + SIZE = 32 + depth = 24 + s1 = pygame.Surface((SIZE, SIZE), 0, depth) + s2 = pygame.Surface((SIZE, SIZE), 0, depth) + s3 = pygame.Surface((SIZE, SIZE), 0, depth) + s1.fill((10, 10, 70, 255)) + s2.fill((10, 20, 70, 255)) + s3.fill((10, 130, 10, 255)) + + surfaces = [s1, s2, s3] + sr = pygame.transform.average_surfaces(surfaces) + self.assertEqual(sr.get_masks(), s1.get_masks()) + self.assertEqual(sr.get_flags(), s1.get_flags()) + self.assertEqual(sr.get_losses(), s1.get_losses()) + + if 0: + print(sr, s1) + print(sr.get_masks(), s1.get_masks()) + print(sr.get_flags(), s1.get_flags()) + print(sr.get_losses(), s1.get_losses()) + print(sr.get_shifts(), s1.get_shifts()) + + self.assertEqual(sr.get_at((0, 0)), (10, 53, 50, 255)) + + def test_average_surfaces__subclassed_surfaces(self): + """Ensure average_surfaces accepts subclassed surfaces.""" + expected_size = (23, 17) + expected_flags = 0 + expected_depth = 32 + expected_color = (50, 50, 50, 255) + surfaces = [] + + for color in ((40, 60, 40), (60, 40, 60)): + s = test_utils.SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) + s.fill(color) + surfaces.append(s) + + surface = pygame.transform.average_surfaces(surfaces) + + self.assertIsInstance(surface, pygame.Surface) + self.assertNotIsInstance(surface, test_utils.SurfaceSubclass) + self.assertEqual(surface.get_at((0, 0)), expected_color) + self.assertEqual(surface.get_bitsize(), expected_depth) + self.assertEqual(surface.get_size(), expected_size) + self.assertEqual(surface.get_flags(), expected_flags) + + def test_average_surfaces__subclassed_destination_surface(self): + """Ensure average_surfaces accepts a destination subclassed surface.""" + expected_size = (13, 27) + expected_flags = 0 + expected_depth = 32 + expected_color = (15, 15, 15, 255) + surfaces = [] + + for color in ((10, 10, 20), (20, 20, 10), (30, 30, 30)): + s = test_utils.SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) + s.fill(color) + surfaces.append(s) + expected_dest_surface = surfaces.pop() + + dest_surface = pygame.transform.average_surfaces( + surfaces, expected_dest_surface + ) + + self.assertIsInstance(dest_surface, pygame.Surface) + self.assertIsInstance(dest_surface, test_utils.SurfaceSubclass) + self.assertIs(dest_surface, expected_dest_surface) + self.assertEqual(dest_surface.get_at((0, 0)), expected_color) + self.assertEqual(dest_surface.get_bitsize(), expected_depth) + self.assertEqual(dest_surface.get_size(), expected_size) + self.assertEqual(dest_surface.get_flags(), expected_flags) + + def test_average_color(self): + """ + """ + + a = [24, 32] + for i in a: + s = pygame.Surface((32, 32), 0, i) + s.fill((0, 100, 200)) + s.fill((10, 50, 100), (0, 0, 16, 32)) + + self.assertEqual(pygame.transform.average_color(s), (5, 75, 150, 0)) + self.assertEqual( + pygame.transform.average_color(s, (16, 0, 16, 32)), (0, 100, 200, 0) + ) + + def todo_test_rotate(self): + + # __doc__ (as of 2008-06-25) for pygame.transform.rotate: + + # pygame.transform.rotate(Surface, angle): return Surface + # rotate an image + + # color = (128, 128, 128, 255) + # s = pygame.Surface((3, 3)) + + # s.set_at((2, 0), color) + + # self.assertNotEqual(s.get_at((0, 0)), color) + # s = pygame.transform.rotate(s, 90) + # self.assertEqual(s.get_at((0, 0)), color) + + self.fail() + + def test_rotate__lossless_at_90_degrees(self): + w, h = 32, 32 + s = pygame.Surface((w, h), pygame.SRCALPHA) + + gradient = list(test_utils.gradient(w, h)) + + for pt, color in gradient: + s.set_at(pt, color) + + for rotation in (90, -90): + s = pygame.transform.rotate(s, rotation) + + for pt, color in gradient: + self.assertTrue(s.get_at(pt) == color) + + def test_scale2x(self): + + # __doc__ (as of 2008-06-25) for pygame.transform.scale2x: + + # pygame.transform.scale2x(Surface, DestSurface = None): Surface + # specialized image doubler + + w, h = 32, 32 + s = pygame.Surface((w, h), pygame.SRCALPHA, 32) + + # s.set_at((0,0), (20, 20, 20, 255)) + + s2 = pygame.transform.scale2x(s) + self.assertEqual(s2.get_rect().size, (64, 64)) + + def test_scale2xraw(self): + w, h = 32, 32 + s = pygame.Surface((w, h), pygame.SRCALPHA, 32) + s.fill((0, 0, 0)) + pygame.draw.circle(s, (255, 0, 0), (w // 2, h // 2), (w // 3)) + + s2 = pygame.transform.scale(s, (w * 2, h * 2)) + s2_2 = pygame.transform.scale(s2, (w * 4, h * 4)) + s4 = pygame.transform.scale(s, (w * 4, h * 4)) + + self.assertEqual(s2_2.get_rect().size, (128, 128)) + + for pt in test_utils.rect_area_pts(s2_2.get_rect()): + self.assertEqual(s2_2.get_at(pt), s4.get_at(pt)) + + def test_get_smoothscale_backend(self): + filter_type = pygame.transform.get_smoothscale_backend() + self.assertTrue(filter_type in ["GENERIC", "MMX", "SSE"]) + # It would be nice to test if a non-generic type corresponds to an x86 + # processor. But there is no simple test for this. platform.machine() + # returns process version specific information, like 'i686'. + + def test_set_smoothscale_backend(self): + # All machines should allow 'GENERIC'. + original_type = pygame.transform.get_smoothscale_backend() + pygame.transform.set_smoothscale_backend("GENERIC") + filter_type = pygame.transform.get_smoothscale_backend() + self.assertEqual(filter_type, "GENERIC") + # All machines should allow returning to original value. + # Also check that keyword argument works. + pygame.transform.set_smoothscale_backend(type=original_type) + # Something invalid. + def change(): + pygame.transform.set_smoothscale_backend("mmx") + + self.assertRaises(ValueError, change) + # Invalid argument keyword. + def change(): + pygame.transform.set_smoothscale_backend(t="GENERIC") + + self.assertRaises(TypeError, change) + # Invalid argument type. + def change(): + pygame.transform.set_smoothscale_backend(1) + + self.assertRaises(TypeError, change) + # Unsupported type, if possible. + if original_type != "SSE": + + def change(): + pygame.transform.set_smoothscale_backend("SSE") + + self.assertRaises(ValueError, change) + # Should be back where we started. + filter_type = pygame.transform.get_smoothscale_backend() + self.assertEqual(filter_type, original_type) + + def todo_test_chop(self): + + # __doc__ (as of 2008-08-02) for pygame.transform.chop: + + # pygame.transform.chop(Surface, rect): return Surface + # gets a copy of an image with an interior area removed + # + # Extracts a portion of an image. All vertical and horizontal pixels + # surrounding the given rectangle area are removed. The corner areas + # (diagonal to the rect) are then brought together. (The original + # image is not altered by this operation.) + # + # NOTE: If you want a "crop" that returns the part of an image within + # a rect, you can blit with a rect to a new surface or copy a + # subsurface. + + self.fail() + + def test_rotozoom(self): + + # __doc__ (as of 2008-08-02) for pygame.transform.rotozoom: + + # pygame.transform.rotozoom(Surface, angle, scale): return Surface + # filtered scale and rotation + # + # This is a combined scale and rotation transform. The resulting + # Surface will be a filtered 32-bit Surface. The scale argument is a + # floating point value that will be multiplied by the current + # resolution. The angle argument is a floating point value that + # represents the counterclockwise degrees to rotate. A negative + # rotation angle will rotate clockwise. + + s = pygame.Surface((10, 0)) + pygame.transform.scale(s, (10, 2)) + s1=pygame.transform.rotozoom(s, 30, 1) + + self.assertEqual(s1.get_rect(), pygame.Rect(0,0,0,0)) + + def todo_test_smoothscale(self): + # __doc__ (as of 2008-08-02) for pygame.transform.smoothscale: + + # pygame.transform.smoothscale(Surface, (width, height), DestSurface = + # None): return Surface + # + # scale a surface to an arbitrary size smoothly + # + # Uses one of two different algorithms for scaling each dimension of + # the input surface as required. For shrinkage, the output pixels are + # area averages of the colors they cover. For expansion, a bilinear + # filter is used. For the amd64 and i686 architectures, optimized MMX + # routines are included and will run much faster than other machine + # types. The size is a 2 number sequence for (width, height). This + # function only works for 24-bit or 32-bit surfaces. An exception + # will be thrown if the input surface bit depth is less than 24. + # + # New in pygame 1.8 + + self.fail() + + +class TransformDisplayModuleTest(unittest.TestCase): + def setUp(self): + pygame.display.init() + + def tearDown(self): + pygame.display.quit() + + def test_flip(self): + """ honors the set_color key on the returned surface from flip. + """ + from pygame.tests.test_utils import example_path + + pygame.display.set_mode((320, 200)) + + fullname = example_path("data/chimp.bmp") + image_loaded = pygame.image.load(fullname) + + image = pygame.Surface(image_loaded.get_size(), 0, 32) + image.blit(image_loaded, (0, 0)) + + image_converted = image_loaded.convert() + + self.assertFalse(image.get_flags() & pygame.SRCALPHA) + self.assertFalse(image_converted.get_flags() & pygame.SRCALPHA) + + surf = pygame.Surface(image.get_size(), 0, 32) + surf2 = pygame.Surface(image.get_size(), 0, 32) + + surf.fill((255, 255, 255)) + surf2.fill((255, 255, 255)) + + colorkey = image.get_at((0, 0)) + image.set_colorkey(colorkey, RLEACCEL) + timage = pygame.transform.flip(image, 1, 0) + + colorkey = image_converted.get_at((0, 0)) + image_converted.set_colorkey(colorkey, RLEACCEL) + timage_converted = pygame.transform.flip(image_converted, 1, 0) + + # blit the flipped surface, and non flipped surface. + surf.blit(timage, (0, 0)) + surf2.blit(image, (0, 0)) + + # the results should be the same. + self.assertEqual(surf.get_at((0, 0)), surf2.get_at((0, 0))) + self.assertEqual(surf2.get_at((0, 0)), (255, 255, 255, 255)) + + # now we test the convert() ed image also works. + surf.fill((255, 255, 255)) + surf2.fill((255, 255, 255)) + surf.blit(timage_converted, (0, 0)) + surf2.blit(image_converted, (0, 0)) + self.assertEqual(surf.get_at((0, 0)), surf2.get_at((0, 0))) + + def test_flip_alpha(self): + """ returns a surface with the same properties as the input. + """ + from pygame.tests.test_utils import example_path + + pygame.display.set_mode((320, 200)) + + fullname = example_path("data/chimp.bmp") + image_loaded = pygame.image.load(fullname) + + image_alpha = pygame.Surface(image_loaded.get_size(), pygame.SRCALPHA, 32) + image_alpha.blit(image_loaded, (0, 0)) + + surf = pygame.Surface(image_loaded.get_size(), 0, 32) + surf2 = pygame.Surface(image_loaded.get_size(), 0, 32) + + colorkey = image_alpha.get_at((0, 0)) + image_alpha.set_colorkey(colorkey, RLEACCEL) + timage_alpha = pygame.transform.flip(image_alpha, 1, 0) + + self.assertTrue(image_alpha.get_flags() & pygame.SRCALPHA) + self.assertTrue(timage_alpha.get_flags() & pygame.SRCALPHA) + + # now we test the alpha image works. + surf.fill((255, 255, 255)) + surf2.fill((255, 255, 255)) + surf.blit(timage_alpha, (0, 0)) + surf2.blit(image_alpha, (0, 0)) + self.assertEqual(surf.get_at((0, 0)), surf2.get_at((0, 0))) + self.assertEqual(surf2.get_at((0, 0)), (255, 0, 0, 255)) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/version_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/version_test.py new file mode 100644 index 0000000..4b2d8ed --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/version_test.py @@ -0,0 +1,43 @@ +import os +import unittest + + +pg_header = os.path.join("src_c", "include", "_pygame.h") + + +class VersionTest(unittest.TestCase): + @unittest.skipIf( + not os.path.isfile(pg_header), "Skipping because we cannot find _pygame.h" + ) + def test_pg_version_consistency(self): + from pygame import version + + pgh_major = -1 + pgh_minor = -1 + pgh_patch = -1 + import re + + major_exp_search = re.compile(r"define\s+PG_MAJOR_VERSION\s+([0-9]+)").search + minor_exp_search = re.compile(r"define\s+PG_MINOR_VERSION\s+([0-9]+)").search + patch_exp_search = re.compile(r"define\s+PG_PATCH_VERSION\s+([0-9]+)").search + with open(pg_header) as f: + for line in f: + if pgh_major == -1: + m = major_exp_search(line) + if m: + pgh_major = int(m.group(1)) + if pgh_minor == -1: + m = minor_exp_search(line) + if m: + pgh_minor = int(m.group(1)) + if pgh_patch == -1: + m = patch_exp_search(line) + if m: + pgh_patch = int(m.group(1)) + self.assertEqual(pgh_major, version.vernum[0]) + self.assertEqual(pgh_minor, version.vernum[1]) + self.assertEqual(pgh_patch, version.vernum[2]) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/tests/video_test.py b/Display/.venv/lib/python3.7/site-packages/pygame/tests/video_test.py new file mode 100644 index 0000000..7cfdf81 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/tests/video_test.py @@ -0,0 +1,21 @@ +import unittest +import pygame +from pygame._sdl2 import video + + +class VideoModuleTest(unittest.TestCase): + default_caption = "pygame window" + + def test_renderer_set_viewport(self): + """ works. + """ + window = video.Window(title=self.default_caption, size=(800, 600)) + renderer = video.Renderer(window=window) + renderer.logical_size = (1920, 1080) + rect = pygame.Rect(0, 0, 1920, 1080) + renderer.set_viewport(rect) + self.assertEqual(renderer.get_viewport(), (0, 0, 1920, 1080)) + + +if __name__ == "__main__": + unittest.main() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/threads/Py25Queue.py b/Display/.venv/lib/python3.7/site-packages/pygame/threads/Py25Queue.py new file mode 100644 index 0000000..603c1bd --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/threads/Py25Queue.py @@ -0,0 +1,216 @@ +"""A multi-producer, multi-consumer queue.""" + +from time import time as _time + +from collections import deque + +__all__ = ['Empty', 'Full', 'Queue'] + +class Empty(Exception): + "Exception raised by Queue.get(block=0)/get_nowait()." + pass + +class Full(Exception): + "Exception raised by Queue.put(block=0)/put_nowait()." + pass + +class Queue: + """Create a queue object with a given maximum size. + + If maxsize is <= 0, the queue size is infinite. + """ + def __init__(self, maxsize=0): + try: + import threading + except ImportError: + import dummy_threading as threading + self._init(maxsize) + # mutex must be held whenever the queue is mutating. All methods + # that acquire mutex must release it before returning. mutex + # is shared between the three conditions, so acquiring and + # releasing the conditions also acquires and releases mutex. + self.mutex = threading.Lock() + # Notify not_empty whenever an item is added to the queue; a + # thread waiting to get is notified then. + self.not_empty = threading.Condition(self.mutex) + # Notify not_full whenever an item is removed from the queue; + # a thread waiting to put is notified then. + self.not_full = threading.Condition(self.mutex) + # Notify all_tasks_done whenever the number of unfinished tasks + # drops to zero; thread waiting to join() is notified to resume + self.all_tasks_done = threading.Condition(self.mutex) + self.unfinished_tasks = 0 + + def task_done(self): + """Indicate that a formerly enqueued task is complete. + + Used by Queue consumer threads. For each get() used to fetch a task, + a subsequent call to task_done() tells the queue that the processing + on the task is complete. + + If a join() is currently blocking, it will resume when all items + have been processed (meaning that a task_done() call was received + for every item that had been put() into the queue). + + Raises a ValueError if called more times than there were items + placed in the queue. + """ + self.all_tasks_done.acquire() + try: + unfinished = self.unfinished_tasks - 1 + if unfinished <= 0: + if unfinished < 0: + raise ValueError('task_done() called too many times') + self.all_tasks_done.notifyAll() + self.unfinished_tasks = unfinished + finally: + self.all_tasks_done.release() + + def join(self): + """Blocks until all items in the Queue have been gotten and processed. + + The count of unfinished tasks goes up whenever an item is added to the + queue. The count goes down whenever a consumer thread calls task_done() + to indicate the item was retrieved and all work on it is complete. + + When the count of unfinished tasks drops to zero, join() unblocks. + """ + self.all_tasks_done.acquire() + try: + while self.unfinished_tasks: + self.all_tasks_done.wait() + finally: + self.all_tasks_done.release() + + def qsize(self): + """Return the approximate size of the queue (not reliable!).""" + self.mutex.acquire() + n = self._qsize() + self.mutex.release() + return n + + def empty(self): + """Return True if the queue is empty, False otherwise (not reliable!).""" + self.mutex.acquire() + n = self._empty() + self.mutex.release() + return n + + def full(self): + """Return True if the queue is full, False otherwise (not reliable!).""" + self.mutex.acquire() + n = self._full() + self.mutex.release() + return n + + def put(self, item, block=True, timeout=None): + """Put an item into the queue. + + If optional args 'block' is true and 'timeout' is None (the default), + block if necessary until a free slot is available. If 'timeout' is + a positive number, it blocks at most 'timeout' seconds and raises + the Full exception if no free slot was available within that time. + Otherwise ('block' is false), put an item on the queue if a free slot + is immediately available, else raise the Full exception ('timeout' + is ignored in that case). + """ + self.not_full.acquire() + try: + if not block: + if self._full(): + raise Full + elif timeout is None: + while self._full(): + self.not_full.wait() + else: + if timeout < 0: + raise ValueError("'timeout' must be a positive number") + endtime = _time() + timeout + while self._full(): + remaining = endtime - _time() + if remaining <= 0.0: + raise Full + self.not_full.wait(remaining) + self._put(item) + self.unfinished_tasks += 1 + self.not_empty.notify() + finally: + self.not_full.release() + + def put_nowait(self, item): + """Put an item into the queue without blocking. + + Only enqueue the item if a free slot is immediately available. + Otherwise raise the Full exception. + """ + return self.put(item, False) + + def get(self, block=True, timeout=None): + """Remove and return an item from the queue. + + If optional args 'block' is true and 'timeout' is None (the default), + block if necessary until an item is available. If 'timeout' is + a positive number, it blocks at most 'timeout' seconds and raises + the Empty exception if no item was available within that time. + Otherwise ('block' is false), return an item if one is immediately + available, else raise the Empty exception ('timeout' is ignored + in that case). + """ + self.not_empty.acquire() + try: + if not block: + if self._empty(): + raise Empty + elif timeout is None: + while self._empty(): + self.not_empty.wait() + else: + if timeout < 0: + raise ValueError("'timeout' must be a positive number") + endtime = _time() + timeout + while self._empty(): + remaining = endtime - _time() + if remaining <= 0.0: + raise Empty + self.not_empty.wait(remaining) + item = self._get() + self.not_full.notify() + return item + finally: + self.not_empty.release() + + def get_nowait(self): + """Remove and return an item from the queue without blocking. + + Only get an item if one is immediately available. Otherwise + raise the Empty exception. + """ + return self.get(False) + + # Override these methods to implement other queue organizations + # (e.g. stack or priority queue). + # These will only be called with appropriate locks held + + # Initialize the queue representation + def _init(self, maxsize): + self.maxsize = maxsize + self.queue = deque() + + def _qsize(self): + return len(self.queue) + + # Check whether the queue is empty + def _empty(self): + return not self.queue + + # Check whether the queue is full + def _full(self): + return self.maxsize > 0 and len(self.queue) == self.maxsize + + # Put a new item in the queue + def _put(self, item): + self.queue.append(item) + + # Get an item from the queue + def _get(self): + return self.queue.popleft() diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/threads/__init__.py b/Display/.venv/lib/python3.7/site-packages/pygame/threads/__init__.py new file mode 100644 index 0000000..d0a39cb --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/threads/__init__.py @@ -0,0 +1,310 @@ +""" +* Experimental * + +Like the map function, but can use a pool of threads. + +Really easy to use threads. eg. tmap(f, alist) + +If you know how to use the map function, you can use threads. +""" + +__author__ = "Rene Dudfield" +__version__ = "0.3.0" +__license__ = 'Python license' + +import traceback, sys + +from pygame.compat import geterror + +if sys.version_info[0] == 3: + from queue import Queue + from queue import Empty +elif (sys.version_info[0] == 2 and sys.version_info[1] < 5): + from Py25Queue import Queue + from Py25Queue import Empty +else: + # use up to date version + from Queue import Queue + from Queue import Empty + +import threading +Thread = threading.Thread + +STOP = object() +FINISH = object() + +# DONE_ONE = object() +# DONE_TWO = object() + +# a default worker queue. +_wq = None + +# if we are using threads or not. This is the number of workers. +_use_workers = 0 + +# Set this to the maximum for the amount of Cores/CPUs +# Note, that the tests early out. +# So it should only test the best number of workers +2 +MAX_WORKERS_TO_TEST = 64 + + + +def init(number_of_workers = 0): + """ Does a little test to see if threading is worth it. + Sets up a global worker queue if it's worth it. + + Calling init() is not required, but is generally better to do. + """ + global _wq, _use_workers + + if number_of_workers: + _use_workers = number_of_workers + else: + _use_workers = benchmark_workers() + + # if it is best to use zero workers, then use that. + _wq = WorkerQueue(_use_workers) + + + + +def quit(): + """ cleans up everything. + """ + global _wq, _use_workers + _wq.stop() + _wq = None + _use_workers = False + + +def benchmark_workers(a_bench_func = None, the_data = None): + """ does a little test to see if workers are at all faster. + Returns the number of workers which works best. + Takes a little bit of time to run, so you should only really call + it once. + You can pass in benchmark data, and functions if you want. + a_bench_func - f(data) + the_data - data to work on. + """ + global _use_workers + + #TODO: try and make this scale better with slower/faster cpus. + # first find some variables so that using 0 workers takes about 1.0 seconds. + # then go from there. + + + # note, this will only work with pygame 1.8rc3+ + # replace the doit() and the_data with something that releases the GIL + + + import pygame + import pygame.transform + import time + + if not a_bench_func: + def doit(x): + return pygame.transform.scale(x, (544, 576)) + else: + doit = a_bench_func + + if not the_data: + thedata = [] + for x in range(10): + thedata.append(pygame.Surface((155,155), 0, 32)) + else: + thedata = the_data + + best = time.time() + 100000000 + best_number = 0 + #last_best = -1 + + for num_workers in range(0, MAX_WORKERS_TO_TEST): + + wq = WorkerQueue(num_workers) + t1 = time.time() + for xx in range(20): + print ("active count:%s" % threading.activeCount()) + tmap(doit, thedata, worker_queue = wq) + t2 = time.time() + + wq.stop() + + + total_time = t2 - t1 + print ("total time num_workers:%s: time:%s:" % (num_workers, total_time)) + + if total_time < best: + #last_best = best_number + best_number =num_workers + best = total_time + + if num_workers - best_number > 1: + # We tried to add more, but it didn't like it. + # so we stop with testing at this number. + break + + + return best_number + + + + +class WorkerQueue(object): + + def __init__(self, num_workers = 20): + self.queue = Queue() + self.pool = [] + self._setup_workers(num_workers) + + def _setup_workers(self, num_workers): + """ Sets up the worker threads + NOTE: undefined behaviour if you call this again. + """ + self.pool = [] + + for _ in range(num_workers): + self.pool.append(Thread(target=self.threadloop)) + + for a_thread in self.pool: + a_thread.setDaemon(True) + a_thread.start() + + + def do(self, f, *args, **kwArgs): + """ puts a function on a queue for running later. + """ + self.queue.put((f, args, kwArgs)) + + + def stop(self): + """ Stops the WorkerQueue, waits for all of the threads to finish up. + """ + self.queue.put(STOP) + for thread in self.pool: + thread.join() + + + def threadloop(self): #, finish = False): + """ Loops until all of the tasks are finished. + """ + while True: + args = self.queue.get() + if args is STOP: + self.queue.put(STOP) + self.queue.task_done() + break + else: + try: + args[0](*args[1], **args[2]) + finally: + # clean up the queue, raise the exception. + self.queue.task_done() + #raise + + + def wait(self): + """ waits until all tasks are complete. + """ + self.queue.join() + +class FuncResult: + """ Used for wrapping up a function call so that the results are stored + inside the instances result attribute. + """ + def __init__(self, f, callback = None, errback = None): + """ f - is the function we that we call + callback(result) - this is called when the function(f) returns + errback(exception) - this is called when the function(f) raises + an exception. + """ + self.f = f + self.exception = None + self.callback = callback + self.errback = errback + + def __call__(self, *args, **kwargs): + + #we try to call the function here. If it fails we store the exception. + try: + self.result = self.f(*args, **kwargs) + if self.callback: + self.callback(self.result) + except Exception: + self.exception = geterror() + if self.errback: + self.errback(self.exception) + + +def tmap(f, seq_args, num_workers = 20, worker_queue = None, wait = True, stop_on_error = True): + """ like map, but uses a thread pool to execute. + num_workers - the number of worker threads that will be used. If pool + is passed in, then the num_workers arg is ignored. + worker_queue - you can optionally pass in an existing WorkerQueue. + wait - True means that the results are returned when everything is finished. + False means that we return the [worker_queue, results] right away instead. + results, is returned as a list of FuncResult instances. + stop_on_error - + """ + + if worker_queue: + wq = worker_queue + else: + # see if we have a global queue to work with. + if _wq: + wq = _wq + else: + if num_workers == 0: + return map(f, seq_args) + + wq = WorkerQueue(num_workers) + + # we short cut it here if the number of workers is 0. + # normal map should be faster in this case. + if len(wq.pool) == 0: + return map(f, seq_args) + + #print ("queue size:%s" % wq.queue.qsize()) + + + #TODO: divide the data (seq_args) into even chunks and + # then pass each thread a map(f, equal_part(seq_args)) + # That way there should be less locking, and overhead. + + + + results = [] + for sa in seq_args: + results.append(FuncResult(f)) + wq.do(results[-1], sa) + + + #wq.stop() + + if wait: + #print ("wait") + wq.wait() + #print ("after wait") + #print ("queue size:%s" % wq.queue.qsize()) + if wq.queue.qsize(): + raise Exception("buggy threadmap") + # if we created a worker queue, we need to stop it. + if not worker_queue and not _wq: + #print ("stoping") + wq.stop() + if wq.queue.qsize(): + um = wq.queue.get() + if not um is STOP: + raise Exception("buggy threadmap") + + + # see if there were any errors. If so raise the first one. This matches map behaviour. + # TODO: the traceback doesn't show up nicely. + # NOTE: TODO: we might want to return the results anyway? This should be an option. + if stop_on_error: + error_ones = list(filter(lambda x:x.exception, results)) + if error_ones: + raise error_ones[0].exception + + return map(lambda x:x.result, results) + else: + return [wq, results] diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/time.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/time.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..2bb7fbc Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/time.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/time.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/time.pyi new file mode 100644 index 0000000..2e68671 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/time.pyi @@ -0,0 +1,16 @@ +from typing import Optional, Union +from pygame.event import Event + +def get_ticks() -> int: ... +def wait(milliseconds: int) -> int: ... +def delay(milliseconds: int) -> int: ... +def set_timer( + event_type: Union[int, Event], milliseconds: int, once: Optional[bool] = False +) -> None: ... + +class Clock: + def tick(self, framerate: Optional[int] = 0) -> int: ... + def tick_busy_loop(self, framerate: Optional[int] = 0) -> int: ... + def get_time(self) -> int: ... + def get_rawtime(self) -> int: ... + def get_fps(self) -> float: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/transform.cpython-37m-x86_64-linux-gnu.so b/Display/.venv/lib/python3.7/site-packages/pygame/transform.cpython-37m-x86_64-linux-gnu.so new file mode 100755 index 0000000..bfb2513 Binary files /dev/null and b/Display/.venv/lib/python3.7/site-packages/pygame/transform.cpython-37m-x86_64-linux-gnu.so differ diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/transform.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/transform.pyi new file mode 100644 index 0000000..0a672a4 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/transform.pyi @@ -0,0 +1,50 @@ +from typing import Tuple, List, Union, Optional, Sequence +from pygame.surface import Surface +from pygame.math import Vector2 +from pygame.color import Color +from pygame.rect import Rect + +_Coordinate = Union[Tuple[float, float], List[float], Vector2] +_ColorValue = Union[ + Color, Tuple[int, int, int], List[int], int, Tuple[int, int, int, int] +] +_RectValue = Union[ + Rect, + Union[Tuple[int, int, int, int], List[int]], + Union[Tuple[_Coordinate, _Coordinate], List[_Coordinate]], +] + +def flip(surface: Surface, xbool: bool, ybool: bool) -> Surface: ... +def scale( + surface: Surface, + size: Union[Tuple[int, int], List[int]], + dest_surface: Optional[Surface] = None, +) -> Surface: ... +def rotate(surface: Surface, angle: float) -> Surface: ... +def rotozoom(surface: Surface, angle: float, scale: float) -> Surface: ... +def scale2x(surface: Surface, dest_surface: Optional[Surface] = None) -> Surface: ... +def smoothscale( + surface: Surface, + size: Union[Tuple[int, int], List[int]], + dest_surface: Optional[Surface] = None, +) -> Surface: ... +def get_smoothscale_backend() -> str: ... +def set_smoothscale_backend(value: str) -> None: ... +def chop(surface: Surface, rect: _RectValue) -> Surface: ... +def laplacian(surface: Surface, dest_surface: Surface) -> Surface: ... +def average_surfaces( + surfaces: Sequence[Surface], + dest_surface: Optional[Surface] = None, + palette_colors: Optional[Union[bool, int]] = 1, +) -> Surface: ... +def average_color(surface: Surface, rect: Optional[_RectValue]) -> Color: ... +def threshold( + dest_surface: Surface, + surf: Surface, + search_color: _ColorValue, + threshold: Optional[_ColorValue] = (0, 0, 0, 0), + set_color: Optional[_ColorValue] = (0, 0, 0, 0), + set_behavior: Optional[int] = 1, + search_surf: Optional[Surface] = None, + inverse_set: Optional[bool] = False, +) -> int: ... diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/version.py b/Display/.venv/lib/python3.7/site-packages/pygame/version.py new file mode 100644 index 0000000..a19f54a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/version.py @@ -0,0 +1,47 @@ +## pygame - Python Game Library +## Copyright (C) 2000-2003 Pete Shinners +## +## This library is free software; you can redistribute it and/or +## modify it under the terms of the GNU Library General Public +## License as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## Library General Public License for more details. +## +## You should have received a copy of the GNU Library General Public +## License along with this library; if not, write to the Free +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## +## Pete Shinners +## pete@shinners.org + +"""Simply the current installed pygame version. The version information is +stored in the regular pygame module as 'pygame.ver'. Keeping the version +information also available in a separate module allows you to test the +pygame version without importing the main pygame module. + +The python version information should always compare greater than any previous +releases. (hmm, until we get to versions > 10) +""" + +class PygameVersion(tuple): + __slots__ = () + fields = 'major', 'minor', 'patch' + def __new__(cls, major, minor, patch): + return tuple.__new__(cls, (major, minor, patch)) + def __repr__(self): + fields = ('{}={}'.format(fld, val) for fld, val in zip(self.fields, self)) + return '{}({})'.format(str(self.__class__.__name__), ', '.join(fields)) + def __str__(self): + return '{}.{}.{}'.format(*self) + major = property(lambda self: self[0]) + minor = property(lambda self: self[1]) + patch = property(lambda self: self[2]) +ver = "2.0.0.dev8" +vernum = PygameVersion(2, 0, 0) +rev = "" + +__all__ = ["ver", "vernum", "rev"] diff --git a/Display/.venv/lib/python3.7/site-packages/pygame/version.pyi b/Display/.venv/lib/python3.7/site-packages/pygame/version.pyi new file mode 100644 index 0000000..71932fc --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pygame/version.pyi @@ -0,0 +1,5 @@ +from typing import Tuple + +ver: str +vernum: Tuple[int, int, int] +rev: str diff --git a/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/COPYING b/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/COPYING new file mode 100644 index 0000000..b7b5f53 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/INSTALLER b/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/METADATA b/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/METADATA new file mode 100644 index 0000000..5e19131 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/METADATA @@ -0,0 +1,204 @@ +Metadata-Version: 2.1 +Name: pylint +Version: 2.5.3 +Summary: python code static checker +Home-page: https://github.com/PyCQA/pylint +Author: Python Code Quality Authority +Author-email: code-quality@python.org +License: GPL +Platform: UNKNOWN +Classifier: Development Status :: 6 - Mature +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: GNU General Public License (GPL) +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Debuggers +Classifier: Topic :: Software Development :: Quality Assurance +Classifier: Topic :: Software Development :: Testing +Requires-Python: >=3.5.* +Requires-Dist: astroid (<=2.5,>=2.4.0) +Requires-Dist: isort (<5,>=4.2.5) +Requires-Dist: mccabe (<0.7,>=0.6) +Requires-Dist: toml (>=0.7.1) +Requires-Dist: colorama ; sys_platform=="win32" + + +README for Pylint - http://pylint.pycqa.org/ +============================================ + +.. image:: https://travis-ci.org/PyCQA/pylint.svg?branch=master + :target: https://travis-ci.org/PyCQA/pylint + +.. image:: https://ci.appveyor.com/api/projects/status/rbvwhakyj1y09atb/branch/master?svg=true + :alt: AppVeyor Build Status + :target: https://ci.appveyor.com/project/PCManticore/pylint + +.. image:: https://coveralls.io/repos/github/PyCQA/pylint/badge.svg?branch=master + :target: https://coveralls.io/github/PyCQA/pylint?branch=master + + +.. image:: https://img.shields.io/pypi/v/pylint.svg + :alt: Pypi Package version + :target: https://pypi.python.org/pypi/pylint + +.. image:: https://readthedocs.org/projects/pylint/badge/?version=latest + :target: http://pylint.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/ambv/black + +.. |tideliftlogo| image:: doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png + :width: 75 + :height: 60 + :alt: Tidelift + +.. list-table:: + :widths: 10 100 + + * - |tideliftlogo| + - Professional support for pylint is available as part of the `Tidelift + Subscription`_. Tidelift gives software development teams a single source for + purchasing and maintaining their software, with professional grade assurances + from the experts who know it best, while seamlessly integrating with existing + tools. + +.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-pylint?utm_source=pypi-pylint&utm_medium=referral&utm_campaign=readme + + +====== +Pylint +====== + +**It's not just a linter that annoys you!** + +Pylint is a Python static code analysis tool which looks for programming errors, +helps enforcing a coding standard, sniffs for code smells and offers simple refactoring +suggestions. + +It's highly configurable, having special pragmas to control its errors and warnings +from within your code, as well as from an extensive configuration file. +It is also possible to write your own plugins for adding your own checks or for +extending pylint in one way or another. + +It's a free software distributed under the GNU General Public Licence unless +otherwise specified. + +Development is hosted on GitHub: https://github.com/PyCQA/pylint/ + +You can use the code-quality@python.org mailing list to discuss about +Pylint. Subscribe at https://mail.python.org/mailman/listinfo/code-quality/ +or read the archives at https://mail.python.org/pipermail/code-quality/ + +Pull requests are amazing and most welcome. + +Install +------- + +Pylint can be simply installed by running:: + + pip install pylint + +If you are using Python 3.6+, upgrade to get full support for your version:: + + pip install pylint --upgrade + +If you want to install from a source distribution, extract the tarball and run +the following command :: + + python setup.py install + + +Do make sure to do the same for astroid, which is used internally by pylint. + +For debian and rpm packages, use your usual tools according to your Linux distribution. + +More information about installation and available distribution format +can be found here_. + +Documentation +------------- + +The documentation lives at http://pylint.pycqa.org/. + +Pylint is shipped with following additional commands: + +* pyreverse: an UML diagram generator +* symilar: an independent similarities checker +* epylint: Emacs and Flymake compatible Pylint + + +Testing +------- + +We use tox_ for running the test suite. You should be able to install it with:: + + pip install tox pytest + + +To run the test suite for a particular Python version, you can do:: + + tox -e py37 + + +To run individual tests with ``tox``, you can do:: + + tox -e py37 -- -k name_of_the_test + + +We use pytest_ for testing ``pylint``, which you can use without using ``tox`` for a faster development cycle. + +If you want to run tests on a specific portion of the code with pytest_, (pytest-cov_) and your local python version:: + + # ( pip install pytest-cov ) + # Everything: + python3 -m pytest tests/ + # Everything in tests/message with coverage for the relevant code: + python3 -m pytest tests/message/ --cov=pylint.message + coverage html + # Only the functional test "missing_kwoa_py3": + python3 -m pytest "tests/test_functional.py::test_functional[missing_kwoa_py3]" + + +Do not forget to clone astroid_ and install the last version:: + + + git clone https://github.com/PyCQA/astroid.git + + # From source + python3 astroid/setup.py build sdist + pip3 install astroid/dist/astroid*.tar.gz + + # Using an editable installation + cd astroid + python3 -m pip install -e . + + +For more detailed information, check the documentation. + +.. _here: http://pylint.pycqa.org/en/latest/user_guide/installation.html +.. _tox: https://tox.readthedocs.io/en/latest/ +.. _pytest: https://docs.pytest.org/en/latest/ +.. _pytest-cov: https://pypi.org/project/pytest-cov/ +.. _astroid: https://github.com/PyCQA/astroid + +License +------- + +pylint is, with a few exceptions listed below, `GPLv2 `_. + +The icon files are licensed under the `CC BY-SA 4.0 `_ license: + +- `doc/logo.png `_ +- `doc/logo.svg `_ + + diff --git a/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/RECORD b/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/RECORD new file mode 100644 index 0000000..099b7d6 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/RECORD @@ -0,0 +1,173 @@ +../../../bin/epylint,sha256=pvleMlpridIzxhqKKStB4mb3lPnkH8KVmQwE9YJvQlI,279 +../../../bin/pylint,sha256=5AIn89yH3Y1604p4s6Os-V1SLQSodMv_x58mVRWgfBU,277 +../../../bin/pyreverse,sha256=w1uoEP1q9BaaNV2NlGKoVI8qAFYi13wIzelcBeGgMao,283 +../../../bin/symilar,sha256=t1oKSfpRxEbrXDzY3A43Ax0ot0mWar0fTAFfzKc-g18,279 +pylint-2.5.3.data/scripts/epylint,sha256=ebDphNeMoKus049k5MQbxN1JYsHUsOXZxws0Do6gCG0,51 +pylint-2.5.3.data/scripts/pylint,sha256=wXf1V2_-AB_S1uuYztSS90GiTeCkJ4eBOGEQ7CO2Nmc,53 +pylint-2.5.3.data/scripts/pyreverse,sha256=4UQf7-hfOAx6Ux8d5g0d2KIjpUPRMwFhBdsKsu0gWg0,59 +pylint-2.5.3.data/scripts/symilar,sha256=iz6DGtePyfs0haoFobDfsRsMjaFOizh7E3vsevB2Ipw,55 +pylint-2.5.3.dist-info/COPYING,sha256=w4runjyMTV1ZT_VIob4FRTAjAW1ihpMfZRLbIV7B_UI,17989 +pylint-2.5.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pylint-2.5.3.dist-info/METADATA,sha256=0_UScAQEn4WvrPURriRlFU23peXZod5CMjYS3C7TFBo,6630 +pylint-2.5.3.dist-info/RECORD,, +pylint-2.5.3.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92 +pylint-2.5.3.dist-info/entry_points.txt,sha256=WUTwHM2ZcExO-VSvss18AMFsQL-XcWg6O3_MwobWfmw,137 +pylint-2.5.3.dist-info/top_level.txt,sha256=j6Z9i__pIuaiCka6Ul9YIy6yI5aw5QbCtldLvZlMksE,7 +pylint/__init__.py,sha256=v4P6u-dsxp2oq7QRVH29e7BT4M5bdn-8YZbwjebaWe0,1159 +pylint/__main__.py,sha256=HRja-2If7jX6FMF5dY7uMVUO2kJ3I0Y0uflK2ckDA5s,585 +pylint/__pkginfo__.py,sha256=y86DXnCDr25MS_N0l812qvIlvQKJSq8xFbt2XNYet1k,3849 +pylint/__pycache__/__init__.cpython-37.pyc,, +pylint/__pycache__/__main__.cpython-37.pyc,, +pylint/__pycache__/__pkginfo__.cpython-37.pyc,, +pylint/__pycache__/config.cpython-37.pyc,, +pylint/__pycache__/constants.cpython-37.pyc,, +pylint/__pycache__/epylint.cpython-37.pyc,, +pylint/__pycache__/exceptions.cpython-37.pyc,, +pylint/__pycache__/graph.cpython-37.pyc,, +pylint/__pycache__/interfaces.cpython-37.pyc,, +pylint/__pycache__/testutils.cpython-37.pyc,, +pylint/checkers/__init__.py,sha256=93_HWRt86zWkJBZ3rBjL5--AjzBo-PKrK0Us7e7XTVI,2197 +pylint/checkers/__pycache__/__init__.cpython-37.pyc,, +pylint/checkers/__pycache__/async.cpython-37.pyc,, +pylint/checkers/__pycache__/base.cpython-37.pyc,, +pylint/checkers/__pycache__/base_checker.cpython-37.pyc,, +pylint/checkers/__pycache__/classes.cpython-37.pyc,, +pylint/checkers/__pycache__/design_analysis.cpython-37.pyc,, +pylint/checkers/__pycache__/exceptions.cpython-37.pyc,, +pylint/checkers/__pycache__/format.cpython-37.pyc,, +pylint/checkers/__pycache__/imports.cpython-37.pyc,, +pylint/checkers/__pycache__/logging.cpython-37.pyc,, +pylint/checkers/__pycache__/misc.cpython-37.pyc,, +pylint/checkers/__pycache__/newstyle.cpython-37.pyc,, +pylint/checkers/__pycache__/python3.cpython-37.pyc,, +pylint/checkers/__pycache__/raw_metrics.cpython-37.pyc,, +pylint/checkers/__pycache__/refactoring.cpython-37.pyc,, +pylint/checkers/__pycache__/similar.cpython-37.pyc,, +pylint/checkers/__pycache__/spelling.cpython-37.pyc,, +pylint/checkers/__pycache__/stdlib.cpython-37.pyc,, +pylint/checkers/__pycache__/strings.cpython-37.pyc,, +pylint/checkers/__pycache__/typecheck.cpython-37.pyc,, +pylint/checkers/__pycache__/utils.cpython-37.pyc,, +pylint/checkers/__pycache__/variables.cpython-37.pyc,, +pylint/checkers/async.py,sha256=p-Eo8kuMpv-D8Ju1p3FAAKrN3P2OIuBpNM_QxMFce2A,3518 +pylint/checkers/base.py,sha256=nbCvfVc_2kUjHdJlRBFCw1dX7w2Pp5jzn00vnQ3jHC8,96248 +pylint/checkers/base_checker.py,sha256=J8_vh3YS0KnmRpn6SLErE_bCkK14_Qo51dao8SeuLfU,7664 +pylint/checkers/classes.py,sha256=RAiGs_3bN7fLpSq9DE8jwVTfFpQzIEQ7sv5NrzUoTsw,79023 +pylint/checkers/design_analysis.py,sha256=CroyjJL_p8EZJSbzUXENmxJRvAdRvhLPjXAYhXbk_pU,16861 +pylint/checkers/exceptions.py,sha256=po-yCiLgcENySykGtvggcJ2EVi0HnANiHOjzqxhPwTM,21410 +pylint/checkers/format.py,sha256=TohqE-1UL9MILls66yPhkvpKfs9GdA3Ig1dXYq7nPPQ,50762 +pylint/checkers/imports.py,sha256=XKt0y7Zt9W0SiLy0yF5SbG3Gt_j5WcwzN82DA0CkAog,38328 +pylint/checkers/logging.py,sha256=t_EM5pASCXB4ZLpgVm1O4WEXCP08K8ZzyFCqSTfz33M,15919 +pylint/checkers/misc.py,sha256=TgulOYPQmsw2vAvxupeUhmiJwDWgkVooA9A14GqhXW8,7241 +pylint/checkers/newstyle.py,sha256=riJx_9jAoEsH5zM1BtKY_onPb1HaTtQBV9mZhTl8Xos,5067 +pylint/checkers/python3.py,sha256=MhvMXOJlXx_wVBf3srRqV5yMc7qCq6yei66KCr9JwTU,52982 +pylint/checkers/raw_metrics.py,sha256=NS94geCbYOaMIBAb3fPx4cvtgA0D3lcEIumu7IbgtyI,3959 +pylint/checkers/refactoring.py,sha256=B3n4U6Umk524fiUjlZGLgxo9YoQNNOB-2CzggMLMc5U,62100 +pylint/checkers/similar.py,sha256=9knYGfdMxrIwVYsmh-vfQgTfuqfkdqsRIwjsGNIAyrg,15246 +pylint/checkers/spelling.py,sha256=X9fkJ3Zb-19FyKHhiQwNwg4OATap__g2b9-nT6GW_H4,13806 +pylint/checkers/stdlib.py,sha256=inYP_-gCwyp1mvM0KbwInSYh1ADxsXHIZpPh9Rw3ZA0,17638 +pylint/checkers/strings.py,sha256=l6XUbUnCd7AAPIolKoguMYwN7rb6EF714MyJxtYSNcw,38062 +pylint/checkers/typecheck.py,sha256=y1ddtgKq7RgZYB5xqu8PIYndQziwOu-dF6vxjJgRJLo,71565 +pylint/checkers/utils.py,sha256=7FvvkWgZSkIFOmlaWccw7r3Kfpe1_ytXR_2Vcu94y4o,44152 +pylint/checkers/variables.py,sha256=R99QLWXmALpvb-ip20d8XaPWrq4B9mvo9FK1gjOvWuo,79895 +pylint/config.py,sha256=bbMpgmnq52NR0JmxdoHQyrCbDTdAvr3FJOG9KpvuvoY,34938 +pylint/constants.py,sha256=lZDGbin0MViT0UrLSGX2xUqjEw9RkwFISkzfoR12H8Y,1160 +pylint/epylint.py,sha256=SO_Cq-Ljraq2Ozh7I3QQO3U3LSf6NH3AuXXwzk6tGTs,7173 +pylint/exceptions.py,sha256=4KJ3js3zC5PnwTsqnW94Rt_fs-p1-2HtNPU2FP1u3MQ,1147 +pylint/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pylint/extensions/__pycache__/__init__.cpython-37.pyc,, +pylint/extensions/__pycache__/_check_docs_utils.cpython-37.pyc,, +pylint/extensions/__pycache__/bad_builtin.cpython-37.pyc,, +pylint/extensions/__pycache__/broad_try_clause.cpython-37.pyc,, +pylint/extensions/__pycache__/check_docs.cpython-37.pyc,, +pylint/extensions/__pycache__/check_elif.cpython-37.pyc,, +pylint/extensions/__pycache__/comparetozero.cpython-37.pyc,, +pylint/extensions/__pycache__/docparams.cpython-37.pyc,, +pylint/extensions/__pycache__/docstyle.cpython-37.pyc,, +pylint/extensions/__pycache__/emptystring.cpython-37.pyc,, +pylint/extensions/__pycache__/mccabe.cpython-37.pyc,, +pylint/extensions/__pycache__/overlapping_exceptions.cpython-37.pyc,, +pylint/extensions/__pycache__/redefined_variable_type.cpython-37.pyc,, +pylint/extensions/_check_docs_utils.py,sha256=3vXllnOf3srL4NZSKQy7K95DT7u82-v7gf9Kg4zI3ds,23564 +pylint/extensions/bad_builtin.py,sha256=-qe_DBnzeNAfgaNSNlikWyLPL9W5gQUmkFsEGQZGW84,2504 +pylint/extensions/broad_try_clause.py,sha256=CsnpS0ueP4nWa7a8BTM-w7flYoOht-3TPHaYGQgNC2A,2326 +pylint/extensions/check_docs.py,sha256=ljSFJOUK0xwRBvmpZOHXsosX4iKKa_YwTM0VPLvrmJc,790 +pylint/extensions/check_elif.py,sha256=bUUOv8Rb_AZ-JddYnX4dBkmYhMse8qTcrDgsNa5ZRmQ,2614 +pylint/extensions/comparetozero.py,sha256=SsAGaUuJ7cTWz6IUwnRzTxFNBljKbDaRmmdpeZhcFqE,2463 +pylint/extensions/docparams.py,sha256=XTJf40-_JCQcjjCW0Jw0kRehtks47330FsYSDsjVLKE,20370 +pylint/extensions/docstyle.py,sha256=9qztfXfRj_iCYdBpqqZrMbkhej20GaWBIIBCq0ZWaYE,3029 +pylint/extensions/emptystring.py,sha256=ppgholCCXo8Q5qFJ3YzU-oBudwp62eykMSpaYFn-luQ,2562 +pylint/extensions/mccabe.py,sha256=rlUuJkdH7EzCUPnR1AOrQwgxo8HR3TONU519xAdt9m8,6278 +pylint/extensions/overlapping_exceptions.py,sha256=Qsi7qIF6sx36HYV8cjKjPcgYRZNPiFIg4NEcefjGFyg,3266 +pylint/extensions/redefined_variable_type.py,sha256=RrG--uAQvt5P855SEB7B8aVtb18TSR_mrbSUyhgV8WQ,4327 +pylint/graph.py,sha256=mX7SgOWtcTu1HQ2SO1WNe1Z2CahPTlWvoKpdiU1mCFA,6380 +pylint/interfaces.py,sha256=4LN9EpgGhVLu1np9h3ZlBE7wZV_fGD5AK8PS8692occ,3229 +pylint/lint/__init__.py,sha256=FkrvfVY1gTb6syeGkvNDM37-0pXXOmR8qu0ig95CQ9M,4188 +pylint/lint/__pycache__/__init__.cpython-37.pyc,, +pylint/lint/__pycache__/check_parallel.cpython-37.pyc,, +pylint/lint/__pycache__/pylinter.cpython-37.pyc,, +pylint/lint/__pycache__/report_functions.cpython-37.pyc,, +pylint/lint/__pycache__/run.cpython-37.pyc,, +pylint/lint/__pycache__/utils.cpython-37.pyc,, +pylint/lint/check_parallel.py,sha256=NnYhOChDtxzIBfRKxnk1tKFItf0rHWcPrpHt7z9N8f4,3870 +pylint/lint/pylinter.py,sha256=8MlZMfWHuQ7MmDLMMksndhN9tjLQ6wgpOdj15qY8oTs,46060 +pylint/lint/report_functions.py,sha256=4jB7bRHENFdvY4xXMamIC1VaQdrYdyp-629KujV-pI8,2750 +pylint/lint/run.py,sha256=D2ZOyUJ0EUzakGzYfJNLkAJuu6HTjZPQo7HDrqmNHt0,16904 +pylint/lint/utils.py,sha256=glQ5R9CNfpDL6Kj9lysnlUOOpOGuMP_sLA2gUpA1m2w,2308 +pylint/message/__init__.py,sha256=9uvFPjKSPpBPNfrk3Gg5ruh-GG9T5sw-pvThvy13c2U,2801 +pylint/message/__pycache__/__init__.cpython-37.pyc,, +pylint/message/__pycache__/message.cpython-37.pyc,, +pylint/message/__pycache__/message_definition.cpython-37.pyc,, +pylint/message/__pycache__/message_definition_store.cpython-37.pyc,, +pylint/message/__pycache__/message_handler_mix_in.cpython-37.pyc,, +pylint/message/__pycache__/message_id_store.cpython-37.pyc,, +pylint/message/message.py,sha256=gWfzKhNbsj8hOQXNhjI3nbT3TT08Al0aPj48aySIUBo,1299 +pylint/message/message_definition.py,sha256=o7zckciMEunKoi3wz7U2m507HsXD4IeHuPJ-anURyi8,2993 +pylint/message/message_definition_store.py,sha256=aQi_4Z_tEw9lxQyn07-w_BapJLd04OcZIJ5A4tLgxho,3535 +pylint/message/message_handler_mix_in.py,sha256=BcHlarVGm-ySZOmMOKSmJ-jQlQhVXA6HYpFw4p1_ZYc,15208 +pylint/message/message_id_store.py,sha256=Nri4iRo9t5QefzJP9MU0phz66dBZ3ienVNRqQNQoDac,5291 +pylint/pyreverse/__init__.py,sha256=runafCn0veg0di-i8TztMGlKEJO3Qg01MICGqDgZ0c0,202 +pylint/pyreverse/__pycache__/__init__.cpython-37.pyc,, +pylint/pyreverse/__pycache__/diadefslib.cpython-37.pyc,, +pylint/pyreverse/__pycache__/diagrams.cpython-37.pyc,, +pylint/pyreverse/__pycache__/inspector.cpython-37.pyc,, +pylint/pyreverse/__pycache__/main.cpython-37.pyc,, +pylint/pyreverse/__pycache__/utils.cpython-37.pyc,, +pylint/pyreverse/__pycache__/vcgutils.cpython-37.pyc,, +pylint/pyreverse/__pycache__/writer.cpython-37.pyc,, +pylint/pyreverse/diadefslib.py,sha256=DLaFAAhSLAqXaeejDlnX5fDwVp_O5ZaCVKVfIvytYbQ,8748 +pylint/pyreverse/diagrams.py,sha256=1EP3fKP171jqEdCPzw0Z6nxqX1Puu7oEMOtxmMB7eHc,9029 +pylint/pyreverse/inspector.py,sha256=qzrTe2AUKczB3bRfIhw7NGtixSZzO6fKMwYmBFBYujI,12257 +pylint/pyreverse/main.py,sha256=DCxc0yzTvGIMrI1iNBr47KG0bodW0ZXAopSRNe27CDU,6528 +pylint/pyreverse/utils.py,sha256=IIAzFkxezNndOmcNqDzXVvfRjzRCv2e_KZZTHR-fOqM,6351 +pylint/pyreverse/vcgutils.py,sha256=_6k4N51UvyosimNMlydnqkARcZvnck05pfFUq7KHTd0,6442 +pylint/pyreverse/writer.py,sha256=10wgUZ-8ANHgCvI2c1ZkukcAeZ-dfbJ_MWL75MoRCuI,8003 +pylint/reporters/__init__.py,sha256=Gay99jJOKgOAaIL-DpUgClg06Zhsdqqy31aGd6G9eXI,1650 +pylint/reporters/__pycache__/__init__.cpython-37.pyc,, +pylint/reporters/__pycache__/base_reporter.cpython-37.pyc,, +pylint/reporters/__pycache__/collecting_reporter.cpython-37.pyc,, +pylint/reporters/__pycache__/json_reporter.cpython-37.pyc,, +pylint/reporters/__pycache__/reports_handler_mix_in.cpython-37.pyc,, +pylint/reporters/__pycache__/text.cpython-37.pyc,, +pylint/reporters/base_reporter.py,sha256=HilNSVW_QAG34uY73b01eLlK-RPMgA62O4gk19rVIm8,2031 +pylint/reporters/collecting_reporter.py,sha256=tFJ0rsTNz9h4C0U5lgbe1jlQAOy0Of0WLGrq2QO7Sug,478 +pylint/reporters/json_reporter.py,sha256=b-g8qxzRAebfPKllQ_3lG3I2EyESxOG-g4wTkYag2X0,1996 +pylint/reporters/reports_handler_mix_in.py,sha256=t4Ucmk5Wa2CagNbEbehlr1n6H-4zmRnrCEvI57qVuig,2704 +pylint/reporters/text.py,sha256=FuiPfFMyRoPCPYpqK53Drdn-TrWYutGUi-hWe_PDS_A,8081 +pylint/reporters/ureports/__init__.py,sha256=gkLD-92S5ZuMLekRIdO3m0e0dzOizeZwBk1gmLUv59Y,3229 +pylint/reporters/ureports/__pycache__/__init__.cpython-37.pyc,, +pylint/reporters/ureports/__pycache__/nodes.cpython-37.pyc,, +pylint/reporters/ureports/__pycache__/text_writer.cpython-37.pyc,, +pylint/reporters/ureports/nodes.py,sha256=kmSiC5qqlREFkhuYwI--rArqvbQ5Q1oOhhoatAVWuFE,5187 +pylint/reporters/ureports/text_writer.py,sha256=ur8elimZFHrBDWBEBHQ931JDA_68WK5f4WwIFk1kJUM,3373 +pylint/testutils.py,sha256=DjvPH1TT0ul3XuXhWB0332JzuJqvtkyAbGt46hSKYPk,20377 +pylint/utils/__init__.py,sha256=QS6DXcOvzXOznUaGjwcbPzh1DQaVVbngOevSF_cm7C4,2882 +pylint/utils/__pycache__/__init__.cpython-37.pyc,, +pylint/utils/__pycache__/ast_walker.cpython-37.pyc,, +pylint/utils/__pycache__/file_state.cpython-37.pyc,, +pylint/utils/__pycache__/pragma_parser.cpython-37.pyc,, +pylint/utils/__pycache__/utils.cpython-37.pyc,, +pylint/utils/ast_walker.py,sha256=6ewsiRTz1W4v9UFm-lXMZoBWEe8b89kZRrSN7bW3aiM,2907 +pylint/utils/file_state.py,sha256=zw-4J5JasNZPhmcTbDUwiqaXpPWCIpPdYizvyGlnvCo,5962 +pylint/utils/pragma_parser.py,sha256=FuAydaJb_O6RMbVVyZg83eACKVuWxurtc5n1Gxz6E3Q,4799 +pylint/utils/utils.py,sha256=5B5IBM-TUMLKygPHMsp92tFIcBsoHnpv7GE7XOzy1eQ,13311 diff --git a/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/WHEEL b/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/WHEEL new file mode 100644 index 0000000..b552003 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.34.2) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/entry_points.txt b/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/entry_points.txt new file mode 100644 index 0000000..063b5e4 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/entry_points.txt @@ -0,0 +1,6 @@ +[console_scripts] +epylint = pylint:run_epylint +pylint = pylint:run_pylint +pyreverse = pylint:run_pyreverse +symilar = pylint:run_symilar + diff --git a/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/top_level.txt b/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/top_level.txt new file mode 100644 index 0000000..7fb0ea1 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pylint-2.5.3.dist-info/top_level.txt @@ -0,0 +1 @@ +pylint diff --git a/Display/.venv/lib/python3.7/site-packages/pylint/__init__.py b/Display/.venv/lib/python3.7/site-packages/pylint/__init__.py new file mode 100644 index 0000000..8c47558 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pylint/__init__.py @@ -0,0 +1,44 @@ +# Copyright (c) 2008, 2012 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2014, 2016-2020 Claudiu Popa +# Copyright (c) 2014 Arun Persaud +# Copyright (c) 2015 Ionel Cristian Maries +# Copyright (c) 2018 Nick Drozd +# Copyright (c) 2020 Pierre Sassoulas + +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/master/COPYING + +import sys + +from pylint.__pkginfo__ import version as __version__ + +# pylint: disable=import-outside-toplevel + + +def run_pylint(): + from pylint.lint import Run as PylintRun + + try: + PylintRun(sys.argv[1:]) + except KeyboardInterrupt: + sys.exit(1) + + +def run_epylint(): + from pylint.epylint import Run as EpylintRun + + EpylintRun() + + +def run_pyreverse(): + """run pyreverse""" + from pylint.pyreverse.main import Run as PyreverseRun + + PyreverseRun(sys.argv[1:]) + + +def run_symilar(): + """run symilar""" + from pylint.checkers.similar import Run as SimilarRun + + SimilarRun(sys.argv[1:]) diff --git a/Display/.venv/lib/python3.7/site-packages/pylint/__main__.py b/Display/.venv/lib/python3.7/site-packages/pylint/__main__.py new file mode 100644 index 0000000..badb500 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pylint/__main__.py @@ -0,0 +1,18 @@ +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/master/COPYING + +#!/usr/bin/env python +import os +import sys + +import pylint + +# Strip out the current working directory from sys.path. +# Having the working directory in `sys.path` means that `pylint` might +# inadvertently import user code from modules having the same name as +# stdlib or pylint's own modules. +# CPython issue: https://bugs.python.org/issue33053 +if sys.path[0] == "" or sys.path[0] == os.getcwd(): + sys.path.pop(0) + +pylint.run_pylint() diff --git a/Display/.venv/lib/python3.7/site-packages/pylint/__pkginfo__.py b/Display/.venv/lib/python3.7/site-packages/pylint/__pkginfo__.py new file mode 100644 index 0000000..c6e2439 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pylint/__pkginfo__.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2006-2015 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2010 Julien Jehannet +# Copyright (c) 2013-2014 Google, Inc. +# Copyright (c) 2014-2020 Claudiu Popa +# Copyright (c) 2014 Brett Cannon +# Copyright (c) 2014 Ricardo Gemignani +# Copyright (c) 2014 Arun Persaud +# Copyright (c) 2015 Ionel Cristian Maries +# Copyright (c) 2016 Moises Lopez +# Copyright (c) 2016 Florian Bruhin +# Copyright (c) 2016 Jakub Wilk +# Copyright (c) 2017-2018 Hugo +# Copyright (c) 2018-2019 Ashley Whetter +# Copyright (c) 2018 ssolanki +# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com> +# Copyright (c) 2019 Ville Skyttä +# Copyright (c) 2019 Hugo van Kemenade +# Copyright (c) 2019 Dan Hemberger <846186+hemberger@users.noreply.github.com> +# Copyright (c) 2019 jab + +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/master/COPYING + +# pylint: disable=redefined-builtin,invalid-name +"""pylint packaging information""" + +from os.path import join + +# For an official release, use dev_version = None +numversion = (2, 5, 3) +dev_version = None + +version = ".".join(str(num) for num in numversion) +if dev_version is not None: + version += "-dev" + str(dev_version) + +install_requires = [ + "astroid>=2.4.0,<=2.5", + "isort>=4.2.5,<5", + "mccabe>=0.6,<0.7", + "toml>=0.7.1", +] + +dependency_links = [] # type: ignore + +extras_require = {} +extras_require[':sys_platform=="win32"'] = ["colorama"] + +license = "GPL" +description = "python code static checker" +web = "https://github.com/PyCQA/pylint" +mailinglist = "mailto:code-quality@python.org" +author = "Python Code Quality Authority" +author_email = "code-quality@python.org" + +classifiers = [ + "Development Status :: 6 - Mature", + "Environment :: Console", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU General Public License (GPL)", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Software Development :: Debuggers", + "Topic :: Software Development :: Quality Assurance", + "Topic :: Software Development :: Testing", +] + + +long_desc = """\ + Pylint is a Python source code analyzer which looks for programming + errors, helps enforcing a coding standard and sniffs for some code + smells (as defined in Martin Fowler's Refactoring book) + . + Pylint can be seen as another PyChecker since nearly all tests you + can do with PyChecker can also be done with Pylint. However, Pylint + offers some more features, like checking length of lines of code, + checking if variable names are well-formed according to your coding + standard, or checking if declared interfaces are truly implemented, + and much more. + . + Additionally, it is possible to write plugins to add your own checks. + . + Pylint is shipped with "pyreverse" (UML diagram generator) + and "symilar" (an independent similarities checker).""" + +scripts = [ + join("bin", filename) for filename in ("pylint", "symilar", "epylint", "pyreverse") +] diff --git a/Display/.venv/lib/python3.7/site-packages/pylint/checkers/__init__.py b/Display/.venv/lib/python3.7/site-packages/pylint/checkers/__init__.py new file mode 100644 index 0000000..5c2be6a --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pylint/checkers/__init__.py @@ -0,0 +1,67 @@ +# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2013-2014 Google, Inc. +# Copyright (c) 2013 buck@yelp.com +# Copyright (c) 2014-2018 Claudiu Popa +# Copyright (c) 2014 Brett Cannon +# Copyright (c) 2014 Arun Persaud +# Copyright (c) 2015 Ionel Cristian Maries +# Copyright (c) 2016 Moises Lopez +# Copyright (c) 2017-2018 Bryce Guinta +# Copyright (c) 2018-2019 Pierre Sassoulas +# Copyright (c) 2018 ssolanki +# Copyright (c) 2019 Bruno P. Kinoshita + +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/master/COPYING + +"""utilities methods and classes for checkers + +Base id of standard checkers (used in msg and report ids): +01: base +02: classes +03: format +04: import +05: misc +06: variables +07: exceptions +08: similar +09: design_analysis +10: newstyle +11: typecheck +12: logging +13: string_format +14: string_constant +15: stdlib +16: python3 +17: refactoring +18-50: not yet used: reserved for future internal checkers. +51-99: perhaps used: reserved for external checkers + +The raw_metrics checker has no number associated since it doesn't emit any +messages nor reports. XXX not true, emit a 07 report ! + +""" + +from pylint.checkers.base_checker import BaseChecker, BaseTokenChecker +from pylint.utils import register_plugins + + +def table_lines_from_stats(stats, _, columns): + """get values listed in from and , + and return a formated list of values, designed to be given to a + ureport.Table object + """ + lines = [] + for m_type in columns: + new = stats[m_type] + new = "%.3f" % new if isinstance(new, float) else str(new) + lines += (m_type.replace("_", " "), new, "NC", "NC") + return lines + + +def initialize(linter): + """initialize linter with checkers in this package """ + register_plugins(linter, __path__[0]) + + +__all__ = ("BaseChecker", "BaseTokenChecker", "initialize") diff --git a/Display/.venv/lib/python3.7/site-packages/pylint/checkers/async.py b/Display/.venv/lib/python3.7/site-packages/pylint/checkers/async.py new file mode 100644 index 0000000..62288c8 --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pylint/checkers/async.py @@ -0,0 +1,90 @@ +# Copyright (c) 2015-2018 Claudiu Popa +# Copyright (c) 2017 Derek Gustafson +# Copyright (c) 2019 Pierre Sassoulas + +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/master/COPYING + +"""Checker for anything related to the async protocol (PEP 492).""" + +import sys + +import astroid +from astroid import bases, exceptions + +from pylint import checkers, interfaces, utils +from pylint.checkers import utils as checker_utils +from pylint.checkers.utils import decorated_with + + +class AsyncChecker(checkers.BaseChecker): + __implements__ = interfaces.IAstroidChecker + name = "async" + msgs = { + "E1700": ( + "Yield inside async function", + "yield-inside-async-function", + "Used when an `yield` or `yield from` statement is " + "found inside an async function.", + {"minversion": (3, 5)}, + ), + "E1701": ( + "Async context manager '%s' doesn't implement __aenter__ and __aexit__.", + "not-async-context-manager", + "Used when an async context manager is used with an object " + "that does not implement the async context management protocol.", + {"minversion": (3, 5)}, + ), + } + + def open(self): + self._ignore_mixin_members = utils.get_global_option( + self, "ignore-mixin-members" + ) + self._async_generators = ["contextlib.asynccontextmanager"] + + @checker_utils.check_messages("yield-inside-async-function") + def visit_asyncfunctiondef(self, node): + for child in node.nodes_of_class(astroid.Yield): + if child.scope() is node and ( + sys.version_info[:2] == (3, 5) or isinstance(child, astroid.YieldFrom) + ): + self.add_message("yield-inside-async-function", node=child) + + @checker_utils.check_messages("not-async-context-manager") + def visit_asyncwith(self, node): + for ctx_mgr, _ in node.items: + inferred = checker_utils.safe_infer(ctx_mgr) + if inferred is None or inferred is astroid.Uninferable: + continue + + if isinstance(inferred, bases.AsyncGenerator): + # Check if we are dealing with a function decorated + # with contextlib.asynccontextmanager. + if decorated_with(inferred.parent, self._async_generators): + continue + else: + try: + inferred.getattr("__aenter__") + inferred.getattr("__aexit__") + except exceptions.NotFoundError: + if isinstance(inferred, astroid.Instance): + # If we do not know the bases of this class, + # just skip it. + if not checker_utils.has_known_bases(inferred): + continue + # Just ignore mixin classes. + if self._ignore_mixin_members: + if inferred.name[-5:].lower() == "mixin": + continue + else: + continue + + self.add_message( + "not-async-context-manager", node=node, args=(inferred.name,) + ) + + +def register(linter): + """required method to auto register this checker""" + linter.register_checker(AsyncChecker(linter)) diff --git a/Display/.venv/lib/python3.7/site-packages/pylint/checkers/base.py b/Display/.venv/lib/python3.7/site-packages/pylint/checkers/base.py new file mode 100644 index 0000000..582b0fd --- /dev/null +++ b/Display/.venv/lib/python3.7/site-packages/pylint/checkers/base.py @@ -0,0 +1,2503 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2006-2016 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2010 Daniel Harding +# Copyright (c) 2012-2014 Google, Inc. +# Copyright (c) 2013-2020 Claudiu Popa +# Copyright (c) 2014 Brett Cannon +# Copyright (c) 2014 Arun Persaud +# Copyright (c) 2015 Nick Bastin +# Copyright (c) 2015 Michael Kefeder +# Copyright (c) 2015 Dmitry Pribysh +# Copyright (c) 2015 Stephane Wirtel +# Copyright (c) 2015 Cosmin Poieana +# Copyright (c) 2015 Florian Bruhin +# Copyright (c) 2015 Radu Ciorba +# Copyright (c) 2015 Ionel Cristian Maries +# Copyright (c) 2016, 2019 Ashley Whetter +# Copyright (c) 2016, 2018 Jakub Wilk +# Copyright (c) 2016-2017 Łukasz Rogalski +# Copyright (c) 2016 Glenn Matthews +# Copyright (c) 2016 Elias Dorneles +# Copyright (c) 2016 Yannack +# Copyright (c) 2016 Alex Jurkiewicz +# Copyright (c) 2017 Pierre Sassoulas +# Copyright (c) 2017, 2019 hippo91 +# Copyright (c) 2017 danields +# Copyright (c) 2017 Jacques Kvam +# Copyright (c) 2017 ttenhoeve-aa +# Copyright (c) 2018-2019 Nick Drozd +# Copyright (c) 2018-2019 Ville Skyttä +# Copyright (c) 2018 Sergei Lebedev <185856+superbobry@users.noreply.github.com> +# Copyright (c) 2018 Lucas Cimon +# Copyright (c) 2018 ssolanki +# Copyright (c) 2018 Natalie Serebryakova +# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com> +# Copyright (c) 2018 SergeyKosarchuk +# Copyright (c) 2018 Steven M. Vascellaro +# Copyright (c) 2018 Mike Frysinger +# Copyright (c) 2018 Chris Lamb +# Copyright (c) 2018 glmdgrielson <32415403+glmdgrielson@users.noreply.github.com> +# Copyright (c) 2019 Daniel Draper +# Copyright (c) 2019 Hugo van Kemenade +# Copyright (c) 2019 Pierre Sassoulas +# Copyright (c) 2019 Niko Wenselowski +# Copyright (c) 2019 Nikita Sobolev +# Copyright (c) 2019 Oisín Moran +# Copyright (c) 2019 Fantix King +# Copyright (c) 2020 Anthony Sottile +# Copyright (c) 2020 bernie gray +# Copyright (c) 2020 Gabriel R Sezefredo +# Copyright (c) 2020 Benny +# Copyright (c) 2020 Anubhav <35621759+anubh-v@users.noreply.github.com> + +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/master/COPYING + +"""basic checker for Python code""" + +import builtins +import collections +import itertools +import re +import sys +from typing import Pattern + +import astroid +import astroid.bases +import astroid.scoped_nodes +from astroid.arguments import CallSite + +import pylint.utils as lint_utils +from pylint import checkers, exceptions, interfaces +from pylint.checkers import utils +from pylint.checkers.utils import ( + is_overload_stub, + is_property_deleter, + is_property_setter, +) +from pylint.reporters.ureports import nodes as reporter_nodes + + +class NamingStyle: + # It may seem counterintuitive that single naming style + # has multiple "accepted" forms of regular expressions, + # but we need to special-case stuff like dunder names + # in method names. + CLASS_NAME_RGX = None # type: Pattern[str] + MOD_NAME_RGX = None # type: Pattern[str] + CONST_NAME_RGX = None # type: Pattern[str] + COMP_VAR_RGX = None # type: Pattern[str] + DEFAULT_NAME_RGX = None # type: Pattern[str] + CLASS_ATTRIBUTE_RGX = None # type: Pattern[str] + + @classmethod + def get_regex(cls, name_type): + return { + "module": cls.MOD_NAME_RGX, + "const": cls.CONST_NAME_RGX, + "class": cls.CLASS_NAME_RGX, + "function": cls.DEFAULT_NAME_RGX, + "method": cls.DEFAULT_NAME_RGX, + "attr": cls.DEFAULT_NAME_RGX, + "argument": cls.DEFAULT_NAME_RGX, + "variable": cls.DEFAULT_NAME_RGX, + "class_attribute": cls.CLASS_ATTRIBUTE_RGX, + "inlinevar": cls.COMP_VAR_RGX, + }[name_type] + + +class SnakeCaseStyle(NamingStyle): + """Regex rules for snake_case naming style.""" + + CLASS_NAME_RGX = re.compile(r"[^\W\dA-Z][^\WA-Z]+$") + MOD_NAME_RGX = re.compile(r"[^\W\dA-Z][^\WA-Z]*$") + CONST_NAME_RGX = re.compile(r"([^\W\dA-Z][^\WA-Z]*|__.*__)$") + COMP_VAR_RGX = re.compile(r"[^\W\dA-Z][^\WA-Z]*$") + DEFAULT_NAME_RGX = re.compile( + r"([^\W\dA-Z][^\WA-Z]{2,}|_[^\WA-Z]*|__[^\WA-Z\d_][^\WA-Z]+__)$" + ) + CLASS_ATTRIBUTE_RGX = re.compile(r"([^\W\dA-Z][^\WA-Z]{2,}|__.*__)$") + + +class CamelCaseStyle(NamingStyle): + """Regex rules for camelCase naming style.""" + + CLASS_NAME_RGX = re.compile(r"[^\W\dA-Z][^\W_]+$") + MOD_NAME_RGX = re.compile(r"[^\W\dA-Z][^\W_]*$") + CONST_NAME_RGX = re.compile(r"([^\W\dA-Z][^\W_]*|__.*__)$") + COMP_VAR_RGX = re.compile(r"[^\W\dA-Z][^\W_]*$") + DEFAULT_NAME_RGX = re.compile(r"([^\W\dA-Z][^\W_]{2,}|__[^\W\dA-Z_]\w+__)$") + CLASS_ATTRIBUTE_RGX = re.compile(r"([^\W\dA-Z][^\W_]{2,}|__.*__)$") + + +class PascalCaseStyle(NamingStyle): + """Regex rules for PascalCase naming style.""" + + CLASS_NAME_RGX = re.compile(r"[^\W\da-z][^\W_]+$") + MOD_NAME_RGX = re.compile(r"[^\W\da-z][^\W_]+$") + CONST_NAME_RGX = re.compile(r"([^\W\da-z][^\W_]*|__.*__)$") + COMP_VAR_RGX = re.compile(r"[^\W\da-z][^\W_]+$") + DEFAULT_NAME_RGX = re.compile(r"([^\W\da-z][^\W_]{2,}|__[^\W\dA-Z_]\w+__)$") + CLASS_ATTRIBUTE_RGX = re.compile(r"[^\W\da-z][^\W_]{2,}$") + + +class UpperCaseStyle(NamingStyle): + """Regex rules for UPPER_CASE naming style.""" + + CLASS_NAME_RGX = re.compile(r"[^\W\da-z][^\Wa-z]+$") + MOD_NAME_RGX = re.compile(r"[^\W\da-z][^\Wa-z]+$") + CONST_NAME_RGX = re.compile(r"([^\W\da-z][^\Wa-z]*|__.*__)$") + COMP_VAR_RGX = re.compile(r"[^\W\da-z][^\Wa-z]+$") + DEFAULT_NAME_RGX = re.compile(r"([^\W\da-z][^\Wa-z]{2,}|__[^\W\dA-Z_]\w+__)$") + CLASS_ATTRIBUTE_RGX = re.compile(r"[^\W\da-z][^\Wa-z]{2,}$") + + +class AnyStyle(NamingStyle): + @classmethod + def get_regex(cls, name_type): + return re.compile(".*") + + +NAMING_STYLES = { + "snake_case": SnakeCaseStyle, + "camelCase": CamelCaseStyle, + "PascalCase": PascalCaseStyle, + "UPPER_CASE": UpperCaseStyle, + "any": AnyStyle, +} + +# do not require a doc string on private/system methods +NO_REQUIRED_DOC_RGX = re.compile("^_") +REVERSED_PROTOCOL_METHOD = "__reversed__" +SEQUENCE_PROTOCOL_METHODS = ("__getitem__", "__len__") +REVERSED_METHODS = (SEQUENCE_PROTOCOL_METHODS, (REVERSED_PROTOCOL_METHOD,)) +TYPECHECK_COMPARISON_OPERATORS = frozenset(("is", "is not", "==", "!=")) +LITERAL_NODE_TYPES = (astroid.Const, astroid.Dict, astroid.List, astroid.Set) +UNITTEST_CASE = "unittest.case" +BUILTINS = builtins.__name__ +TYPE_QNAME = "%s.type" % BUILTINS +ABC_METACLASSES = {"_py_abc.ABCMeta", "abc.ABCMeta"} # Python 3.7+, + +# Name categories that are always consistent with all naming conventions. +EXEMPT_NAME_CATEGORIES = {"exempt", "ignore"} + +# A mapping from qname -> symbol, to be used when generating messages +# about dangerous default values as arguments +DEFAULT_ARGUMENT_SYMBOLS = dict( + zip( + [".".join([BUILTINS, x]) for x in ("set", "dict", "list")], + ["set()", "{}", "[]"], + ), + **{ + x: "%s()" % x + for x in ( + "collections.deque", + "collections.ChainMap", + "collections.Counter", + "collections.OrderedDict", + "collections.defaultdict", + "collections.UserDict", + "collections.UserList", + ) + }, +) +REVERSED_COMPS = {"<": ">", "<=": ">=", ">": "<", ">=": "<="} +COMPARISON_OPERATORS = frozenset(("==", "!=", "<", ">", "<=", ">=")) +# List of methods which can be redefined +REDEFINABLE_METHODS = frozenset(("__module__",)) +TYPING_FORWARD_REF_QNAME = "typing.ForwardRef" + + +def _redefines_import(node): + """ Detect that the given node (AssignName) is inside an + exception handler and redefines an import from the tryexcept body. + Returns True if the node redefines an import, False otherwise. + """ + current = node + while current and not isinstance(current.parent, astroid.ExceptHandler): + current = current.parent + if not current or not utils.error_of_type(current.parent, ImportError): + return False + try_block = current.parent.parent + for import_node in try_block.nodes_of_class((astroid.ImportFrom, astroid.Import)): + for name, alias in import_node.names: + if alias: + if alias == node.name: + return True + elif name == node.name: + return True + return False + + +def in_loop(node): + """return True if the node is inside a kind of for loop""" + parent = node.parent + while parent is not None: + if isinstance( + parent, + ( + astroid.For, + astroid.ListComp, + astroid.SetComp, + astroid.DictComp, + astroid.GeneratorExp, + ), + ): + return True + parent = parent.parent + return False + + +def in_nested_list(nested_list, obj): + """return true if the object is an element of or of a nested + list + """ + for elmt in nested_list: + if isinstance(elmt, (list, tuple)): + if in_nested_list(elmt, obj): + return True + elif elmt == obj: + return True + return False + + +def _get_break_loop_node(break_node): + """ + Returns the loop node that holds the break node in arguments. + + Args: + break_node (astroid.Break): the break node of interest. + + Returns: + astroid.For or astroid.While: the loop node holding the break node. + """ + loop_nodes = (astroid.For, astroid.While) + parent = break_node.parent + while not isinstance(parent, loop_nodes) or break_node in getattr( + parent, "orelse", [] + ): + break_node = parent + parent = parent.parent + if parent is None: + break + return parent + + +def _loop_exits_early(loop): + """ + Returns true if a loop may ends up in a break statement. + + Args: + loop (astroid.For, astroid.While): the loop node inspected. + + Returns: + bool: True if the loop may ends up in a break statement, False otherwise. + """ + loop_nodes = (astroid.For, astroid.While) + definition_nodes = (astroid.FunctionDef, astroid.ClassDef) + inner_loop_nodes = [ + _node + for _node in loop.nodes_of_class(loop_nodes, skip_klass=definition_nodes) + if _node != loop + ] + return any( + _node + for _node in loop.nodes_of_class(astroid.Break, skip_klass=definition_nodes) + if _get_break_loop_node(_node) not in inner_loop_nodes + ) + + +def _is_multi_naming_match(match, node_type, confidence): + return ( + match is not None + and match.lastgroup is not None + and match.lastgroup not in EXEMPT_NAME_CATEGORIES + and (node_type != "method" or confidence != interfaces.INFERENCE_FAILURE) + ) + + +BUILTIN_PROPERTY = "builtins.property" + + +def _get_properties(config): + """Returns a tuple of property classes and names. + + Property classes are fully qualified, such as 'abc.abstractproperty' and + property names are the actual names, such as 'abstract_property'. + """ + property_classes = {BUILTIN_PROPERTY} + property_names = set() # Not returning 'property', it has its own check. + if config is not None: + property_classes.update(config.property_classes) + property_names.update( + prop.rsplit(".", 1)[-1] for prop in config.property_classes + ) + return property_classes, property_names + + +def _determine_function_name_type(node: astroid.FunctionDef, config=None): + """Determine the name type whose regex the a function's name should match. + + :param node: A function node. + :param config: Configuration from which to pull additional property classes. + :type config: :class:`optparse.Values` + + :returns: One of ('function', 'method', 'attr') + :rtype: str + """ + property_classes, property_names = _get_properties(config) + if not node.is_method(): + return "function" + + if is_property_setter(node) or is_property_deleter(node): + # If the function is decorated using the prop_method.{setter,getter} + # form, treat it like an attribute as well. + return "attr" + + if node.decorators: + decorators = node.decorators.nodes + else: + decorators = [] + for decorator in decorators: + # If the function is a property (decorated with @property + # or @abc.abstractproperty), the name type is 'attr'. + if isinstance(decorator, astroid.Name) or ( + isinstance(decorator, astroid.Attribute) + and decorator.attrname in property_names + ): + inferred = utils.safe_infer(decorator) + if ( + inferred + and hasattr(inferred, "qname") + and inferred.qname() in property_classes + ): + return "attr" + return "method" + + +def _has_abstract_methods(node): + """ + Determine if the given `node` has abstract methods. + + The methods should be made abstract by decorating them + with `abc` decorators. + """ + return len(utils.unimplemented_abstract_methods(node)) > 0 + + +def report_by_type_stats(sect, stats, _): + """make a report of + + * percentage of different types documented + * percentage of different types with a bad name + """ + # percentage of different types documented and/or with a bad name + nice_stats = {} + for node_type in ("module", "class", "method", "function"): + try: + total = stats[node_type] + except KeyError: + raise exceptions.EmptyReportError() + nice_stats[node_type] = {} + if total != 0: + try: + documented = total - stats["undocumented_" + node_type] + percent = (documented * 100.0) / total + nice_stats[node_type]["percent_documented"] = "%.2f" % percent + except KeyError: + nice_stats[node_type]["percent_documented"] = "NC" + try: + percent = (stats["badname_" + node_type] * 100.0) / total + nice_stats[node_type]["percent_badname"] = "%.2f" % percent + except KeyError: + nice_stats[node_type]["percent_badname"] = "NC" + lines = ("type", "number", "old number", "difference", "%documented", "%badname") + for node_type in ("module", "class", "method", "function"): + new = stats[node_type] + lines += ( + node_type, + str(new), + "NC", + "NC", + nice_stats[node_type].get("percent_documented", "0"), + nice_stats[node_type].get("percent_badname", "0"), + ) + sect.append(reporter_nodes.Table(children=lines, cols=6, rheaders=1)) + + +def redefined_by_decorator(node): + """return True if the object is a method redefined via decorator. + + For example: + @property + def x(self): return self._x + @x.setter + def x(self, value): self._x = value + """ + if node.decorators: + for decorator in node.decorators.nodes: + if ( + isinstance(decorator, astroid.Attribute) + and getattr(decorator.expr, "name", None) == node.name + ): + return True + return False + + +class _BasicChecker(checkers.BaseChecker): + __implements__ = interfaces.IAstroidChecker + name = "basic" + + +class BasicErrorChecker(_BasicChecker): + msgs = { + "E0100": ( + "__init__ method is a generator", + "init-is-generator", + "Used when the special class method __init__ is turned into a " + "generator by a yield in its body.", + ), + "E0101": ( + "Explicit return in __init__", + "return-in-init", + "Used when the special class method __init__ has an explicit " + "return value.", + ), + "E0102": ( + "%s already defined line %s", + "function-redefined", + "Used when a function / class / method is redefined.", + ), + "E0103": ( + "%r not properly in loop", + "not-in-loop", + "Used when break or continue keywords are used outside a loop.", + ), + "E0104": ( + "Return outside function", + "return-outside-function", + 'Used when a "return" statement is found outside a function or method.', + ), + "E0105": ( + "Yield outside function", + "yield-outside-function", + 'Used when a "yield" statement is found outside a function or method.', + ), + "E0106": ( + "Return with argument inside generator", + "return-arg-in-generator", + 'Used when a "return" statement with an argument is found ' + "outside in a generator function or method (e.g. with some " + '"yield" statements).', + {"maxversion": (3, 3)}, + ), + "E0107": ( + "Use of the non-existent %s operator", + "nonexistent-operator", + "Used when you attempt to use the C-style pre-increment or " + "pre-decrement operator -- and ++, which doesn't exist in Python.", + ), + "E0108": ( + "Duplicate argument name %s in function definition", + "duplicate-argument-name", + "Duplicate argument names in function definitions are syntax errors.", + ), + "E0110": ( + "Abstract class %r with abstract methods instantiated", + "abstract-class-instantiated", + "Used when an abstract class with `abc.ABCMeta` as metaclass " + "has abstract methods and is instantiated.", + ), + "W0120": ( + "Else clause on loop without a break statement", + "useless-else-on-loop", + "Loops should only have an else clause if they can exit early " + "with a break statement, otherwise the statements under else " + "should be on the same scope as the loop itself.", + ), + "E0112": ( + "More than one starred expression in assignment", + "too-many-star-expressions", + "Emitted when there are more than one starred " + "expressions (`*x`) in an assignment. This is a SyntaxError.", + ), + "E0113": ( + "Starred assignment target must be in a list or tuple", + "invalid-star-assignment-target", + "Emitted when a star expression is used as a starred assignment target.", + ), + "E0114": ( + "Can use starred expression only in assignment target", + "star-needs-assignment-target", + "Emitted when a star expression is not used in an assignment target.", + ), + "E0115": ( + "Name %r is nonlocal and global", + "nonlocal-and-global", + "Emitted when a name is both nonlocal and global.", + ), + "E0116": ( + "'continue' not supported inside 'finally' clause", + "continue-in-finally", + "Emitted when the `continue` keyword is found " + "inside a finally clause, which is a SyntaxError.", + {"maxversion": (3, 8)}, + ), + "E0117": ( + "nonlocal name %s found without binding", + "nonlocal-without-binding", + "Emitted when a nonlocal variable does not have an attached " + "name somewhere in the parent scopes", + ), + "E0118": ( + "Name %r is used prior to global declaration", + "used-prior-global-declaration", + "Emitted when a name is used prior a global declaration, " + "which results in an error since Python 3.6.", + {"minversion": (3, 6)}, + ), + } + + @utils.check_messages("function-redefined") + def visit_classdef(self, node): + self._check_redefinition("class", node) + + def _too_many_starred_for_tuple(self, assign_tuple): + starred_count = 0 + for elem in assign_tuple.itered(): + if isinstance(elem, astroid.Tuple): + return self._too_many_starred_for_tuple(elem) + if isinstance(elem, astroid.Starred): + starred_count += 1 + return starred_count > 1 + + @utils.check_messages("too-many-star-expressions", "invalid-star-assignment-target") + def visit_assign(self, node): + # Check *a, *b = ... + assign_target = node.targets[0] + # Check *a = b + if isinstance(node.targets[0], astroid.Starred): + self.add_message("invalid-star-assignment-target", node=node) + + if not isinstance(assign_target, astroid.Tuple): + return + if self._too_many_starred_for_tuple(assign_target): + self.add_message("too-many-star-expressions", node=node) + + @utils.check_messages("star-needs-assignment-target") + def visit_starred(self, node): + """Check that a Starred expression is used in an assignment target.""" + if isinstance(node.parent, astroid.Call): + # f(*args) is converted to Call(args=[Starred]), so ignore + # them for this check. + return + if isinstance( + node.parent, (astroid.List, astroid.Tuple, astroid.Set, astroid.Dict) + ): + # PEP 448 unpacking. + return + + stmt = node.statement() + if not isinstance(stmt, astroid.Assign): + return + + if stmt.value is node or stmt.value.parent_of(node): + self.add_message("star-needs-assignment-target", node=node) + + @utils.check_messages( + "init-is-generator", + "return-in-init", + "function-redefined", + "return-arg-in-generator", + "duplicate-argument-name", + "nonlocal-and-global", + "used-prior-global-declaration", + ) + def visit_functiondef(self, node): + self._check_nonlocal_and_global(node) + self._check_name_used_prior_global(node) + if not redefined_by_decorator( + node + ) and not utils.is_registered_in_singledispatch_function(node): + self._check_redefinition(node.is_method() and "method" or "function", node) + # checks for max returns, branch, return in __init__ + returns = node.nodes_of_class( + astroid.Return, skip_klass=(astroid.FunctionDef, astroid.ClassDef) + ) + if node.is_method() and node.name == "__init__": + if node.is_generator(): + self.add_message("init-is-generator", node=node) + else: + values = [r.value for r in returns] + # Are we returning anything but None from constructors + if any(v for v in values if not utils.is_none(v)): + self.add_message("return-in-init", node=node) + # Check for duplicate names by clustering args with same name for detailed report + arg_clusters = collections.defaultdict(list) + arguments = filter(None, [node.args.args, node.args.kwonlyargs]) + + for arg in itertools.chain.from_iterable(arguments): + arg_clusters[arg.name].append(arg) + + # provide detailed report about each repeated argument + for argument_duplicates in arg_clusters.values(): + if len(argument_duplicates) != 1: + for argument in argument_duplicates: + self.add_message( + "duplicate-argument-name", + line=argument.lineno, + node=argument, + args=(argument.name,), + ) + + visit_asyncfunctiondef = visit_functiondef + + def _check_name_used_prior_global(self, node): + + scope_globals = { + name: child + for child in node.nodes_of_class(astroid.Global) + for name in child.names + if child.scope() is node + } + + if not scope_globals: + return + + for node_name in node.nodes_of_class(astroid.Name): + if node_name.scope() is not node: + continue + + name = node_name.name + corresponding_global = scope_globals.get(name) + if not corresponding_global: + continue + + global_lineno = corresponding_global.fromlineno + if global_lineno and global_lineno > node_name.fromlineno: + self.add_message( + "used-prior-global-declaration", node=node_name, args=(name,) + ) + + def _check_nonlocal_and_global(self, node): + """Check that a name is both nonlocal and global.""" + + def same_scope(current): + return current.scope() is node + + from_iter = itertools.chain.from_iterable + nonlocals = set( + from_iter( + child.names + for child in node.nodes_of_class(astroid.Nonlocal) + if same_scope(child) + ) + ) + + if not nonlocals: + return + + global_vars = set( + from_iter( + child.names + for child in node.nodes_of_class(astroid.Global) + if same_scope(child) + ) + ) + for name in nonlocals.intersection(global_vars): + self.add_message("nonlocal-and-global", args=(name,), node=node) + + @utils.check_messages("return-outside-function") + def visit_return(self, node): + if not isinstance(node.frame(), astroid.FunctionDef): + self.add_message("return-outside-function", node=node) + + @utils.check_messages("yield-outside-function") + def visit_yield(self, node): + self._check_yield_outside_func(node) + + @utils.check_messages("yield-outside-function") + def visit_yieldfrom(self, node): + self._check_yield_outside_func(node) + + @utils.check_messages("not-in-loop", "continue-in-finally") + def visit_continue(self, node): + self._check_in_loop(node, "continue") + + @utils.check_messages("not-in-loop") + def visit_break(self, node): + self._check_in_loop(node, "break") + + @utils.check_messages("useless-else-on-loop") + def visit_for(self, node): + self._check_else_on_loop(node) + + @utils.check_messages("useless-else-on-loop") + def visit_while(self, node): + self._check_else_on_loop(node) + + @utils.check_messages("nonexistent-operator") + def visit_unaryop(self, node): + """check use of the non-existent ++ and -- operator operator""" + if ( + (node.op in "+-") + and isinstance(node.operand, astroid.UnaryOp) + and (node.operand.op == node.op) + ): + self.add_message("nonexistent-operator", node=node, args=node.op * 2) + + def _check_nonlocal_without_binding(self, node, name): + current_scope = node.scope() + while True: + if current_scope.parent is None: + break + + if not isinstance(current_scope, (astroid.ClassDef, astroid.FunctionDef)): + self.add_message("nonlocal-without-binding", args=(name,), node=node) + return + + if name not in current_scope.locals: + current_scope = current_scope.parent.scope() + continue + + # Okay, found it. + return + + if not isinstance(current_scope, astroid.FunctionDef): + self.add_message("nonlocal-without-binding", args=(name,), node=node) + + @utils.check_messages("nonlocal-without-binding") + def visit_nonlocal(self, node): + for name in node.names: + self._check_nonlocal_without_binding(node, name) + + @utils.check_messages("abstract-class-instantiated") + def visit_call(self, node): + """ Check instantiating abstract class with + abc.ABCMeta as metaclass. + """ + try: + for inferred in node.func.infer(): + self._check_inferred_class_is_abstract(inferred, node) + except astroid.InferenceError: + return + + def _check_inferred_class_is_abstract(self, inferred, node): + if not isinstance(inferred, astroid.ClassDef): + return + + klass = utils.node_frame_class(node) + if klass is inferred: + # Don't emit the warning if the class is instantiated + # in its own body or if the call is not an instance + # creation. If the class is instantiated into its own + # body, we're expecting that it knows what it is doing. + return + + # __init__ was called + abstract_methods = _has_abstract_methods(inferred) + + if not abstract_methods: + return + + metaclass = inferred.metaclass() + + if metaclass is None: + # Python 3.4 has `abc.ABC`, which won't be detected + # by ClassNode.metaclass() + for ancestor in inferred.ancestors(): + if ancestor.qname() == "abc.ABC": + self.add_message( + "abstract-class-instantiated", args=(inferred.name,), node=node + ) + break + + return + + if metaclass.qname() in ABC_METACLASSES: + self.add_message( + "abstract-class-instantiated", args=(inferred.name,), node=node + ) + + def _check_yield_outside_func(self, node): + if not isinstance(node.frame(), (astroid.FunctionDef, astroid.Lambda)): + self.add_message("yield-outside-function", node=node) + + def _check_else_on_loop(self, node): + """Check that any loop with an else clause has a break statement.""" + if node.orelse and not _loop_exits_early(node): + self.add_message( + "useless-else-on-loop", + node=node, + # This is not optimal, but the line previous + # to the first statement in the else clause + # will usually be the one that contains the else:. + line=node.orelse[0].lineno - 1, + ) + + def _check_in_loop(self, node, node_name): + """check that a node is inside a for or while loop""" + _node = node.parent + while _node: + if isinstance(_node, (astroid.For, astroid.While)): + if node not in _node.orelse: + return + + if isinstance(_node, (astroid.ClassDef, astroid.FunctionDef)): + break + if ( + isinstance(_node, astroid.TryFinally) + and node in _node.finalbody + and isinstance(node, astroid.Continue) + ): + self.add_message("continue-in-finally", node=node) + + _node = _node.parent + + self.add_message("not-in-loop", node=node, args=node_name) + + def _check_redefinition(self, redeftype, node): + """check for redefinition of a function / method / class name""" + parent_frame = node.parent.frame() + + # Ignore function stubs created for type information + redefinitions = parent_frame.locals[node.name] + defined_self = next( + (local for local in redefinitions if not utils.is_overload_stub(local)), + node, + ) + if defined_self is not node and not astroid.are_exclusive(node, defined_self): + # Additional checks for methods which are not considered + # redefined, since they are already part of the base API. + if ( + isinstance(parent_frame, astroid.ClassDef) + and node.name in REDEFINABLE_METHODS + ): + return + + # Skip typing.overload() functions. + if utils.is_overload_stub(node): + return + + # Exempt functions redefined on a condition. + if isinstance(node.parent, astroid.If): + # Exempt "if not " cases + if ( + isinstance(node.parent.test, astroid.UnaryOp) + and node.parent.test.op == "not" + and isinstance(node.parent.test.operand, astroid.Name) + and node.parent.test.operand.name == node.name + ): + return + + # Exempt "if is not None" cases + # pylint: disable=too-many-boolean-expressions + if ( + isinstance(node.parent.test, astroid.Compare) + and isinstance(node.parent.test.left, astroid.Name) + and node.parent.test.left.name == node.name + and node.parent.test.ops[0][0] == "is" + and isinstance(node.parent.test.ops[0][1], astroid.Const) + and node.parent.test.ops[0][1].value is None + ): + return + + # Check if we have forward references for this node. + try: + redefinition_index = redefinitions.index(node) + except ValueError: + pass + else: + for redefinition in redefinitions[:redefinition_index]: + inferred = utils.safe_infer(redefinition) + if ( + inferred + and isinstance(inferred, astroid.Instance) + and inferred.qname() == TYPING_FORWARD_REF_QNAME + ): + return + + dummy_variables_rgx = lint_utils.get_global_option( + self, "dummy-variables-rgx", default=None + ) + if dummy_variables_rgx and dummy_variables_rgx.match(node.name): + return + self.add_message( + "function-redefined", + node=node, + args=(redeftype, defined_self.fromlineno), + ) + + +class BasicChecker(_BasicChecker): + """checks for : + * doc strings + * number of arguments, local variables, branches, returns and statements in + functions, methods + * required module attributes + * dangerous default values as arguments + * redefinition of function / method / class + * uses of the global statement + """ + + __implements__ = interfaces.IAstroidChecker + + name = "basic" + msgs = { + "W0101": ( + "Unreachable code", + "unreachable", + 'Used when there is some code behind a "return" or "raise" ' + "statement, which will never be accessed.", + ), + "W0102": ( + "Dangerous default value %s as argument", + "dangerous-default-value", + "Used when a mutable value as list or dictionary is detected in " + "a default value for an argument.", + ), + "W0104": ( + "Statement seems to have no effect", + "pointless-statement", + "Used when a statement doesn't have (or at least seems to) any effect.", + ), + "W0105": ( + "String statement has no effect", + "pointless-string-statement", + "Used when a string is used as a statement (which of course " + "has no effect). This is a particular case of W0104 with its " + "own message so you can easily disable it if you're using " + "those strings as documentation, instead of comments.", + ), + "W0106": ( + 'Expression "%s" is assigned to nothing', + "expression-not-assigned", + "Used when an expression that is not a function call is assigned " + "to nothing. Probably something else was intended.", + ), + "W0108": ( + "Lambda may not be necessary", + "unnecessary-lambda", + "Used when the body of a lambda expression is a function call " + "on the same argument list as the lambda itself; such lambda " + "expressions are in all but a few cases replaceable with the " + "function being called in the body of the lambda.", + ), + "W0109": ( + "Duplicate key %r in dictionary", + "duplicate-key", + "Used when a dictionary expression binds the same key multiple times.", + ), + "W0122": ( + "Use of exec", + "exec-used", + 'Used when you use the "exec" statement (function for Python ' + "3), to discourage its usage. That doesn't " + "mean you cannot use it !", + ), + "W0123": ( + "Use of eval", + "eval-used", + 'Used when you use the "eval" function, to discourage its ' + "usage. Consider using `ast.literal_eval` for safely evaluating " + "strings containing Python expressions " + "from untrusted sources. ", + ), + "W0150": ( + "%s statement in finally block may swallow exception", + "lost-exception", + "Used when a break or a return statement is found inside the " + "finally clause of a try...finally block: the exceptions raised " + "in the try clause will be silently swallowed instead of being " + "re-raised.", + ), + "W0199": ( + "Assert called on a 2-item-tuple. Did you mean 'assert x,y'?", + "assert-on-tuple", + "A call of assert on a tuple will always evaluate to true if " + "the tuple is not empty, and will always evaluate to false if " + "it is.", + ), + "W0124": ( + 'Following "as" with another context manager looks like a tuple.', + "confusing-with-statement", + "Emitted when a `with` statement component returns multiple values " + "and uses name binding with `as` only for a part of those values, " + "as in with ctx() as a, b. This can be misleading, since it's not " + "clear if the context manager returns a tuple or if the node without " + "a name binding is another context manager.", + ), + "W0125": ( + "Using a conditional statement with a constant value", + "using-constant-test", + "Emitted when a conditional statement (If or ternary if) " + "uses a constant value for its test. This might not be what " + "the user intended to do.", + ), + "W0126": ( + "Using a conditional statement with potentially wrong function or method call due to missing parentheses", + "missing-parentheses-for-call-in-test", + "Emitted when a conditional statement (If or ternary if) " + "seems to wrongly call a function due to missing parentheses", + ), + "W0127": ( + "Assigning the same variable %r to itself", + "self-assigning-variable", + "Emitted when we detect that a variable is assigned to itself", + ), + "W0128": ( + "Redeclared variable %r in assignment", + "redeclared-assigned-name", + "Emitted when we detect that a variable was redeclared in the same assignment.", + ), + "E0111": ( + "The first reversed() argument is not a sequence", + "bad-reversed-sequence", + "Used when the first argument to reversed() builtin " + "isn't a sequence (does not implement __reversed__, " + "nor __getitem__ and __len__", + ), + "E0119": ( + "format function is not called on str", + "misplaced-format-function", + "Emitted when format function is not called on str object. " + 'e.g doing print("value: {}").format(123) instead of ' + 'print("value: {}".format(123)). This might not be what the user ' + "intended to do.", + ), + "W0129": ( + "Assert statement has a string literal as its first argument. The assert will %s fail.", + "assert-on-string-literal", + "Used when an assert statement has a string literal as its first argument, which will " + "cause the assert to always pass.", + ), + } + + reports = (("RP0101", "Statistics by type", report_by_type_stats),) + + def __init__(self, linter): + _BasicChecker.__init__(self, linter) + self.stats = None + self._tryfinallys = None + + def open(self): + """initialize visit variables and statistics + """ + self._tryfinallys = [] + self.stats = self.linter.add_stats(module=0, function=0, method=0, class_=0) + + @utils.check_messages("using-constant-test", "missing-parentheses-for-call-in-test") + def visit_if(self, node): + self._check_using_constant_test(node, node.test) + + @utils.check_messages("using-constant-test", "missing-parentheses-for-call-in-test") + def visit_ifexp(self, node): + self._check_using_constant_test(node, node.test) + + @utils.check_messages("using-constant-test", "missing-parentheses-for-call-in-test") + def visit_comprehension(self, node): + if node.ifs: + for if_test in node.ifs: + self._check_using_constant_test(node, if_test) + + def _check_using_constant_test(self, node, test): + const_nodes = ( + astroid.Module, + astroid.scoped_nodes.GeneratorExp, + astroid.Lambda, + astroid.FunctionDef, + astroid.ClassDef, + astroid.bases.Generator, + astroid.UnboundMethod, + astroid.BoundMethod, + astroid.Module, + ) + structs = (astroid.Dict, astroid.Tuple, astroid.Set) + + # These nodes are excepted, since they are not constant + # values, requiring a computation to happen. + except_nodes = ( + astroid.Call, + astroid.BinOp, + astroid.BoolOp, + astroid.UnaryOp, + astroid.Subscript, + ) + inferred = None + emit = isinstance(test, (astroid.Const,) + structs + const_nodes) + if not isinstance(test, except_nodes): + inferred = utils.safe_infer(test) + + if emit: + self.add_message("using-constant-test", node=node) + elif isinstance(inferred, const_nodes): + # If the constant node is a FunctionDef or Lambda then + #  it may be a illicit function call due to missing parentheses + call_inferred = None + if isinstance(inferred, astroid.FunctionDef): + call_inferred = inferred.infer_call_result() + elif isinstance(inferred, astroid.Lambda): + call_inferred = inferred.infer_call_result(node) + if call_inferred: + try: + for inf_call in call_inferred: + if inf_call != astroid.Uninferable: + self.add_message( + "missing-parentheses-for-call-in-test", node=node + ) + break + except astroid.InferenceError: + pass + self.add_message("using-constant-test", node=node) + + def visit_module(self, _): + """check module name, docstring and required arguments + """ + self.stats["module"] += 1 + + def visit_classdef(self, node): # pylint: disable=unused-argument + """check module name, docstring and redefinition + increment branch counter + """ + self.stats["class"] += 1 + + @utils.check_messages( + "pointless-statement", "pointless-string-statement", "expression-not-assigned" + ) + def visit_expr(self, node): + """Check for various kind of statements without effect""" + expr = node.value + if isinstance(expr, astroid.Const) and isinstance(expr.value, str): + # treat string statement in a separated message + # Handle PEP-257 attribute docstrings. + # An attribute docstring is defined as being a string right after + # an assignment at the module level, class level or __init__ level. + scope = expr.scope() + if isinstance( + scope, (astroid.ClassDef, astroid.Module, astroid.FunctionDef) + ): + if isinstance(scope, astroid.FunctionDef) and scope.name != "__init__": + pass + else: + sibling = expr.previous_sibling() + if ( + sibling is not None + and sibling.scope() is scope + and isinstance(sibling, (astroid.Assign, astroid.AnnAssign)) + ): + return + self.add_message("pointless-string-statement", node=node) + return + + # Ignore if this is : + # * a direct function call + # * the unique child of a try/except body + # * a yield statement + # * an ellipsis (which can be used on Python 3 instead of pass) + # warn W0106 if we have any underlying function call (we can't predict + # side effects), else pointless-statement + if ( + isinstance( + expr, (astroid.Yield, astroid.Await, astroid.Ellipsis, astroid.Call) + ) + or ( + isinstance(node.parent, astroid.TryExcept) + and node.parent.body == [node] + ) + or (isinstance(expr, astroid.Const) and expr.value is Ellipsis) + ): + return + if any(expr.nodes_of_class(astroid.Call)): + self.add_message( + "expression-not-assigned", node=node, args=expr.as_string() + ) + else: + self.add_message("pointless-statement", node=node) + + @staticmethod + def _filter_vararg(node, call_args): + # Return the arguments for the given call which are + # not passed as vararg. + for arg in call_args: + if isinstance(arg, astroid.Starred): + if ( + isinstance(arg.value, astroid.Name) + and arg.value.name != node.args.vararg + ): + yield arg + else: + yield arg + + @staticmethod + def _has_variadic_argument(args, variadic_name): + if not args: + return True + for arg in args: + if isinstance(arg.value, astroid.Name): + if arg.value.name != variadic_name: + return True + else: + return True + return False + + @utils.check_messages("unnecessary-lambda") + def visit_lambda(self, node): + """check whether or not the lambda is suspicious + """ + # if the body of the lambda is a call expression with the same + # argument list as the lambda itself, then the lambda is + # possibly unnecessary and at least suspicious. + if node.args.defaults: + # If the arguments of the lambda include defaults, then a + # judgment cannot be made because there is no way to check + # that the defaults defined by the lambda are the same as + # the defaults defined by the function called in the body + # of the lambda. + return + call = node.body + if not isinstance(call, astroid.Call): + # The body of the lambda must be a function call expression + # for the lambda to be unnecessary. + return + if isinstance(node.body.func, astroid.Attribute) and isinstance( + node.body.func.expr, astroid.Call + ): + # Chained call, the intermediate call might + # return something else (but we don't check that, yet). + return + + call_site = CallSite.from_call(call) + ordinary_args = list(node.args.args) + new_call_args = list(self._filter_vararg(node, call.args)) + if node.args.kwarg: + if self._has_variadic_argument(call.kwargs, node.args.kwarg): + return + + if node.args.vararg: + if self._has_variadic_argument(call.starargs, node.args.vararg): + return + elif call.starargs: + return + + if call.keywords: + # Look for additional keyword arguments that are not part + # of the lambda's signature + lambda_kwargs = {keyword.name for keyword in node.args.defaults} + if len(lambda_kwargs) != len(call_site.keyword_arguments): + # Different lengths, so probably not identical + return + if set(call_site.keyword_arguments).difference(lambda_kwargs): + return + + # The "ordinary" arguments must be in a correspondence such that: + # ordinary_args[i].name == call.args[i].name. + if len(ordinary_args) != len(new_call_args): + return + for arg, passed_arg in zip(ordinary_args, new_call_args): + if not isinstance(passed_arg, astroid.Name): + return + if arg.name != passed_arg.name: + return + + self.add_message("unnecessary-lambda", line=node.fromlineno, node=node) + + @utils.check_messages("dangerous-default-value") + def visit_functiondef(self, node): + """check function name, docstring, arguments, redefinition, + variable names, max locals + """ + self.stats["method" if node.is_method() else "function"] += 1 + self._check_dangerous_default(node) + + visit_asyncfunctiondef = visit_functiondef + + def _check_dangerous_default(self, node): + # check for dangerous default values as arguments + is_iterable = lambda n: isinstance(n, (astroid.List, astroid.Set, astroid.Dict)) + defaults = node.args.defaults or [] + node.args.kw_defaults or [] + for default in defaults: + if not default: + continue + try: + value = next(default.infer()) + except astroid.InferenceError: + continue + + if ( + isinstance(value, astroid.Instance) + and value.qname() in DEFAULT_ARGUMENT_SYMBOLS + ): + + if value is default: + msg = DEFAULT_ARGUMENT_SYMBOLS[value.qname()] + elif isinstance(value, astroid.Instance) or is_iterable(value): + # We are here in the following situation(s): + # * a dict/set/list/tuple call which wasn't inferred + # to a syntax node ({}, () etc.). This can happen + # when the arguments are invalid or unknown to + # the inference. + # * a variable from somewhere else, which turns out to be a list + # or a dict. + if is_iterable(default): + msg = value.pytype() + elif isinstance(default, astroid.Call): + msg = "%s() (%s)" % (value.name, value.qname()) + else: + msg = "%s (%s)" % (default.as_string(), value.qname()) + else: + # this argument is a name + msg = "%s (%s)" % ( + default.as_string(), + DEFAULT_ARGUMENT_SYMBOLS[value.qname()], + ) + self.add_message("dangerous-default-value", node=node, args=(msg,)) + + @utils.check_messages("unreachable", "lost-exception") + def visit_return(self, node): + """1 - check is the node has a right sibling (if so, that's some + unreachable code) + 2 - check is the node is inside the finally clause of a try...finally + block + """ + self._check_unreachable(node) + # Is it inside final body of a try...finally bloc ? + self._check_not_in_finally(node, "return", (astroid.FunctionDef,)) + + @utils.check_messages("unreachable") + def visit_continue(self, node): + """check is the node has a right sibling (if so, that's some unreachable + code) + """ + self._check_unreachable(node) + + @utils.check_messages("unreachable", "lost-exception") + def visit_break(self, node): + """1 - check is the node has a right sibling (if so, that's some + unreachable code) + 2 - check is the node is inside the finally clause of a try...finally + block + """ + # 1 - Is it right sibling ? + self._check_unreachable(node) + # 2 - Is it inside final body of a try...finally bloc ? + self._check_not_in_finally(node, "break", (astroid.For, astroid.While)) + + @utils.check_messages("unreachable") + def visit_raise(self, node): + """check if the node has a right sibling (if so, that's some unreachable + code) + """ + self._check_unreachable(node) + + @utils.check_messages("exec-used") + def visit_exec(self, node): + """just print a warning on exec statements""" + self.add_message("exec-used", node=node) + + def _check_misplaced_format_function(self, call_node): + if not isinstance(call_node.func, astroid.Attribute): + return + if call_node.func.attrname != "format": + return + + expr = utils.safe_infer(call_node.func.expr) + if expr is astroid.Uninferable: + return + if not expr: + # we are doubtful on inferred type of node, so here just check if format + # was called on print() + call_expr = call_node.func.expr + if not isinstance(call_expr, astroid.Call): + return + if ( + isinstance(call_expr.func, astroid.Name) + and call_expr.func.name == "print" + ): + self.add_message("misplaced-format-function", node=call_node) + + @utils.check_messages( + "eval-used", "exec-used", "bad-reversed-sequence", "misplaced-format-function" + ) + def visit_call(self, node): + """visit a Call node -> check if this is not a blacklisted builtin + call and check for * or ** use + """ + self._check_misplaced_format_function(node) + if isinstance(node.func, astroid.Name): + name = node.func.name + # ignore the name if it's not a builtin (i.e. not defined in the + # locals nor globals scope) + if not (name in node.frame() or name in node.root()): + if name == "exec": + self.add_message("exec-used", node=node) + elif name == "reversed": + self._check_reversed(node) + elif name == "eval": + self.add_message("eval-used", node=node) + + @utils.check_messages("assert-on-tuple", "assert-on-string-literal") + def visit_assert(self, node): + """check whether assert is used on a tuple or string literal.""" + if ( + node.fail is None + and isinstance(node.test, astroid.Tuple) + and len(node.test.elts) == 2 + ): + self.add_message("assert-on-tuple", node=node) + + if isinstance(node.test, astroid.Const) and isinstance(node.test.value, str): + if node.test.value: + when = "never" + else: + when = "always" + self.add_message("assert-on-string-literal", node=node, args=(when,)) + + @utils.check_messages("duplicate-key") + def visit_dict(self, node): + """check duplicate key in dictionary""" + keys = set() + for k, _ in node.items: + if isinstance(k, astroid.Const): + key = k.value + if key in keys: + self.add_message("duplicate-key", node=node, args=key) + keys.add(key) + + def visit_tryfinally(self, node): + """update try...finally flag""" + self._tryfinallys.append(node) + + def leave_tryfinally(self, node): # pylint: disable=unused-argument + """update try...finally flag""" + self._tryfinallys.pop() + + def _check_unreachable(self, node): + """check unreachable code""" + unreach_stmt = node.next_sibling() + if unreach_stmt is not None: + self.add_message("unreachable", node=unreach_stmt) + + def _check_not_in_finally(self, node, node_name, breaker_classes=()): + """check that a node is not inside a finally clause of a + try...finally statement. + If we found before a try...finally bloc a parent which its type is + in breaker_classes, we skip the whole check.""" + # if self._tryfinallys is empty, we're not an in try...finally block + if not self._tryfinallys: + return + # the node could be a grand-grand...-children of the try...finally + _parent = node.parent + _node = node + while _parent and not isinstance(_parent, breaker_classes): + if hasattr(_parent, "finalbody") and _node in _parent.finalbody: + self.add_message("lost-exception", node=node, args=node_name) + return + _node = _parent + _parent = _node.parent + + def _check_reversed(self, node): + """ check that the argument to `reversed` is a sequence """ + try: + argument = utils.safe_infer(utils.get_argument_from_call(node, position=0)) + except utils.NoSuchArgumentError: + pass + else: + if argument is astroid.Uninferable: + return + if argument is None: + # Nothing was inferred. + # Try to see if we have iter(). + if isinstance(node.args[0], astroid.Call): + try: + func = next(node.args[0].func.infer()) + except astroid.InferenceError: + return + if getattr( + func, "name", None + ) == "iter" and utils.is_builtin_object(func): + self.add_message("bad-reversed-sequence", node=node) + return + + if isinstance(argument, (astroid.List, astroid.Tuple)): + return + + if isinstance(argument, astroid.Instance): + if argument._proxied.name == "dict" and utils.is_builtin_object( + argument._proxied + ): + self.add_message("bad-reversed-sequence", node=node) + return + if any( + ancestor.name == "dict" and utils.is_builtin_object(ancestor) + for ancestor in argument._proxied.ancestors() + ): + # Mappings aren't accepted by reversed(), unless + # they provide explicitly a __reversed__ method. + try: + argument.locals[REVERSED_PROTOCOL_METHOD] + except KeyError: + self.add_message("bad-reversed-sequence", node=node) + return + + if hasattr(argument, "getattr"): + # everything else is not a proper sequence for reversed() + for methods in REVERSED_METHODS: + for meth in methods: + try: + argument.getattr(meth) + except astroid.NotFoundError: + break + else: + break + else: + self.add_message("bad-reversed-sequence", node=node) + else: + self.add_message("bad-reversed-sequence", node=node) + + @utils.check_messages("confusing-with-statement") + def visit_with(self, node): + # a "with" statement with multiple managers corresponds + # to one AST "With" node with multiple items + pairs = node.items + if pairs: + for prev_pair, pair in zip(pairs, pairs[1:]): + if isinstance(prev_pair[1], astroid.AssignName) and ( + pair[1] is None and not isinstance(pair[0], astroid.Call) + ): + # Don't emit a message if the second is a function call + # there's no way that can be mistaken for a name assignment. + # If the line number doesn't match + # we assume it's a nested "with". + self.add_message("confusing-with-statement", node=node) + + def _check_self_assigning_variable(self, node): + # Detect assigning to the same variable. + + scope = node.scope() + scope_locals = scope.locals + + rhs_names = [] + targets = node.targets + if isinstance(targets[0], astroid.Tuple): + if len(targets) != 1: + # A complex assignment, so bail out early. + return + targets = targets[0].elts + if len(targets) == 1: + # Unpacking a variable into the same name. + return + + if isinstance(node.value, astroid.Name): + if len(targets) != 1: + return + rhs_names = [node.value] + elif isinstance(node.value, astroid.Tuple): + rhs_count = len(node.value.elts) + if len(targets) != rhs_count or rhs_count == 1: + return + rhs_names = node.value.elts + + for target, lhs_name in zip(targets, rhs_names): + if not isinstance(lhs_name, astroid.Name): + continue + if not isinstance(target, astroid.AssignName): + continue + if isinstance(scope, astroid.ClassDef) and target.name in scope_locals: + # Check that the scope is different than a class level, which is usually + # a pattern to expose module level attributes as class level ones. + continue + if target.name == lhs_name.name: + self.add_message( + "self-assigning-variable", args=(target.name,), node=target + ) + + def _check_redeclared_assign_name(self, targets): + dummy_variables_rgx = lint_utils.get_global_option( + self, "dummy-variables-rgx", default=None + ) + + for target in targets: + if not isinstance(target, astroid.Tuple): + continue + + found_names = [] + for element in target.elts: + if isinstance(element, astroid.Tuple): + self._check_redeclared_assign_name([element]) + elif isinstance(element, astroid.AssignName) and element.name != "_": + if dummy_variables_rgx and dummy_variables_rgx.match(element.name): + return + found_names.append(element.name) + + names = collections.Counter(found_names) + for name, count in names.most_common(): + if count > 1: + self.add_message( + "redeclared-assigned-name", args=(name,), node=target + ) + + @utils.check_messages("self-assigning-variable", "redeclared-assigned-name") + def visit_assign(self, node): + self._check_self_assigning_variable(node) + self._check_redeclared_assign_name(node.targets) + + @utils.check_messages("redeclared-assigned-name") + def visit_for(self, node): + self._check_redeclared_assign_name([node.target]) + + +KNOWN_NAME_TYPES = { + "module", + "const", + "class", + "function", + "method", + "attr", + "argument", + "variable", + "class_attribute", + "inlinevar", +} + + +HUMAN_READABLE_TYPES = { + "module": "module", + "const": "constant", + "class": "class", + "function": "function", + "method": "method", + "attr": "attribute", + "argument": "argument", + "variable": "variable", + "class_attribute": "class attribute", + "inlinevar": "inline iteration", +} + +DEFAULT_NAMING_STYLES = { + "module": "snake_case", + "const": "UPPER_CASE", + "class": "PascalCase", + "function": "snake_case", + "method": "snake_case", + "attr": "snake_case", + "argument": "snake_case", + "variable": "snake_case", + "class_attribute": "any", + "inlinevar": "any", +} + + +def _create_naming_options(): + name_options = [] + for name_type in sorted(KNOWN_NAME_TYPES): + human_readable_name = HUMAN_READABLE_TYPES[name_type] + default_style = DEFAULT_NAMING_STYLES[name_type] + name_type = name_type.replace("_", "-") + name_options.append( + ( + "%s-naming-style" % (name_type,), + { + "default": default_style, + "type": "choice", + "choices": list(NAMING_STYLES.keys()), + "metavar": "